Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Annotations(compiler, assertOnChange) .process(externs, root); } public static class PropogateConstantAnnotations extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; public PropogateConstantAnnotations( AbstractCompiler compiler, boolean forbidChanges) { this.compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.getType() == Token.NAME) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if ((info != null && info.isConstant()) && !n.getBooleanProp(Node.IS_CONSTANT_NAME)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " gramps:" + n.getParent().getParent().toStringTree()); } // Even though the AST has changed (an annotation was added), // the annotations are not compared so don't report the change. // reportCodeChange("constant annotation"); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String,Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; if (NodeUtil.isConstantName(n) || compiler.getCodingConvention().isConstant(n.getString())) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name " + name + " is not annotated as constant."); } else { Preconditions.checkState(expectedConst == isConst, "The name " + name + " should not be annotated as constant."); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name " + name + " is not consistently annotated as " + "constant."); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } // Only inspect the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock(n)) { splitVarDeclarations(n); } if (n.getType() == Token.FUNCTION) { moveNamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.getType() == Token.LABEL); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.FOR: case Token.WHILE: case Token.DO: return; default: Node block = new Node(Token.BLOCK); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (!NodeUtil.isForIn(c) && c.getFirstChild().getType() != Token.EMPTY) { Node init = c.getFirstChild(); c.replaceChild(init, new Node(Token.EMPTY)); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.getType() == Token.VAR) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.getType() == Token.VAR) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = new Node( Token.VAR, name, n.getLineno(), n.getCharno()); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>().getType() == Token.FUNCTION); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Readd the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVar

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>WithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = new Node(Token.ASSIGN, n, value); gramps.replaceChild(parent, new Node(Token.EXPR_RESULT, replacement)); } else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.getType() == Token.FOR) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for (var a;;)..." as those are moved out // as part of the other normalizations. parent.removeChild(n); gramps.replaceChild(parent, n); } else { Preconditions.checkState(gramps.getType() == Token.LABEL); gramps.replaceChild(parent, new Node(Token.EMPTY)); } } reportCodeChange("Duplicate VAR declaration"); } } /** * A simple class that causes scope to be created. */ private final class ScopeTicklingCallback implements NodeTraversal.ScopedCallback { @Override public void enterScope(NodeTraversal t) { // Cause the scope to be created, which will cause duplicate // to be found. t.getScope(); } @Override public void exitScope(NodeTraversal t) { // Nothing to do. } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Nothing to do. } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> addVar(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. if (vIndex >= varStart) { Object v = itsConst.get(vIndex); if (v != null) return DUPLICATE_CONST; else return DUPLICATE_VAR; } else return DUPLICATE_PARAMETER; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(null); itsVariableNames.put(name, index); return NO_DUPLICATE; } public final boolean addConst(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. return false; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(name); itsVariableNames.put(name, index); return true; } public final void removeParamOrVar(String name) { int i = itsVariableNames.get(name, -1); if (i != -1) { itsVariables.remove(i); itsVariableNames.remove(name); ObjToIntMap.Iterator iter = itsVariableNames.newIterator(); for (iter.start(); !iter.done(); iter.next()) { int v = iter.getValue(); if (v > i) { iter.setValue(v - 1); } } } } public final Object getCompilerData() { return compilerData; } public final void setCompilerData(Object data) { if (data == null) throw new IllegalArgumentException(); // Can only call once if (compilerData != null) throw new IllegalStateException(); compilerData = data; } private int encodedSourceStart; private int encodedSourceEnd; private String sourceName; private int baseLineno = -1; private int endLineno = -1; private ObjArray functions; private ObjArray regexps; // a list of the formal parameters and local variables private ObjArray itsVariables

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> is relative only to the current * run of the CodeConsumer and will be normalized * later on by the SourceMap. * * @see SourceMap */ private static class Mapping { Node node; Position start; Position end; } /** * Starts the source mapping for the given * node at the current position. */ @Override void startSourceMapping(Node node) { if (createSrcMap && node.getProp(Node.SOURCEFILE_PROP) != null && node.getLineno() > 0) { int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); // If the index is -1, we are not performing any mapping. if (index >= 0) { Mapping mapping = new Mapping(); mapping.node = node; mapping.start = new Position(line, index); mappings.push(mapping); allMappings.add(mapping); } } } /** * Finishes the source mapping for the given * node at the current position. */ @Override void endSourceMapping(Node node) { if (createSrcMap && node.getProp(Node.SOURCEFILE_PROP) != null && node.getLineno() > 0) { int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); // If the index is -1, we are not performing any mapping. if (index >= 0) { Preconditions.checkState( !mappings.empty(), "Mismatch in start and end of mapping"); Mapping mapping = mappings.pop(); mapping.end = new Position(line, index); } } } /** * Generates the source map from the given code consumer, * appending the information it saved to the SourceMap * object given. */ @Override void generateSourceMap(SourceMap map){ if (createSrcMap) { for (Mapping mapping : allMappings) { map.addMapping(mapping.node, mapping.start, mapping.end); } } } /** * Reports to the code consumer that the given line has been cut at the * given position (i.e. a \n has been inserted there). All mappings in * the source maps after that position will be renormalized

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) { typeOfNode = left; stringNode = right; } else if (right.getType() == Token.TYPEOF && left.getType() == Token.STRING) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().getType() == Token.STRING) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(Node left, Node right, FlowScope blindScope, Function<TypePair, TypePair> merging) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null && ((leftIsRefineable && merged.typeA != null) || (rightIsRefineable && merged.typeB != null))) { FlowScope informed = blindScope.createChildFlowScope(); if (leftIsRefineable && merged.typeA != null) { declareNameInScope(informed, left, merged.typeA); } if (rightIsRefineable && merged.typeB != null) { declareNameInScope(informed, right, merged.typeB); } return informed; } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); } // restricting left type leftType = (leftType == null) ? null : leftType.getRestrictedTypeGivenToBooleanOutcome(condition); if (leftType == null) { return firstPreciserScopeKnowingConditionOutcome(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> right, blindScope, condition); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { rightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope if ((leftType != null && leftIsRefineable) || (rightType != null && rightIsRefineable)) { FlowScope informed = blindScope.createChildFlowScope(); if (leftIsRefineable && leftType != null) { declareNameInScope(informed, left, leftType); } if (rightIsRefineable && rightType != null) { declareNameInScope(informed, right, rightType); } return informed; } } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope); if (leftVar == null) { return blindScope; } FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); rightScope = firstPreciserScopeKnowingConditionOutcome( right, rightScope, !condition); StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope); if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) { return blindScope; } JSType type = leftVar.getType().getLeastSupertype(rightVar.getType()); FlowScope informed = blindScope.createChildFlowScope(); informed.inferSlotType(leftVar.getName(), type); return informed;

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> } private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope, boolean outcome) { JSType type = getTypeIfRefinable(name, blindScope); if (type != null) { JSType restrictedType = type.getRestrictedTypeGivenToBooleanOutcome(outcome); FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, name, restrictedType); return informed; } return blindScope; } private FlowScope caseTypeOf(Node node, JSType type, String value, boolean resultEqualsValue, FlowScope blindScope) { JSType restrictedType = getRestrictedByTypeOfResult(type, value, resultEqualsValue); if (restrictedType == null) { return blindScope; } FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, node, restrictedType); return informed; } private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope, boolean outcome) { JSType leftType = getTypeIfRefinable(left, blindScope); if (leftType == null) { return blindScope; } JSType rightType = right.getJSType(); ObjectType targetType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); if (rightType instanceof FunctionType) { targetType = (FunctionType) rightType; } Visitor<JSType> visitor; if (outcome) { visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); } else { visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); } JSType restrictedLeftType = leftType.visit(visitor); if (restrictedLeftType != null && !restrictedLeftType.equals(leftType)) { FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, left, restrictedLeftType); return informed; } return blindScope; } /** * Given 'property in object', ensures that the object has the property in the * informed scope by defining it as a qualified name if the object type lacks * the property and it's not in the blind scope. * @param object The node of the right-

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>side of the in. * @param propertyName The string of the left-side of the in. */ private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) { JSType jsType = object.getJSType(); jsType = this.getRestrictedWithoutNull(jsType); jsType = this.getRestrictedWithoutUndefined(jsType); boolean hasProperty = false; ObjectType objectType = ObjectType.cast(jsType); if (objectType != null) { hasProperty = objectType.hasProperty(propertyName); } if (!hasProperty) { String qualifiedName = object.getQualifiedName(); if (qualifiedName != null) { String propertyQualifiedName = qualifiedName + "." + propertyName; if (blindScope.getSlot(propertyQualifiedName) == null) { FlowScope informed = blindScope.createChildFlowScope(); JSType unknownType = typeRegistry.getNativeType( JSTypeNative.UNKNOWN_TYPE); informed.inferQualifiedSlot( propertyQualifiedName, unknownType, unknownType); return informed; } } } return blindScope; } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByTrueInstanceOfResultVisitor extends RestrictByTrueTypeOfResultVisitor { private final ObjectType target; RestrictByTrueInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override protected JSType caseTopType(JSType type) { return applyCommonRestriction(type); } @Override public JSType caseUnknownType() { if (target instanceof FunctionType) { FunctionType funcTarget = (FunctionType) target; if (funcTarget.hasInstanceType()) { return funcTarget.getInstanceType(); } } return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return applyCommonRestriction(type); } @Override public JSType caseUnionType(UnionType type) { return applyCommonRestriction(type); } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type); } private JSType applyCommonRestriction(JSType type) { if (target.isUnknownType()) { return type; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { if (n.getType() == Token.EXPR_VOID) { n.setType(Token.EXPR_RESULT); reportChange(); } // Remove unused properties to minimize differences between ASTs // produced by the two parsers. if (n.getType() == Token.FUNCTION) { Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null); } normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && n.getType() != Token.LABEL && n.getType() != Token.SWITCH) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && c.getType() != Token.BLOCK) { Node newBlock = new Node(Token.BLOCK); n.replaceChild(c, newBlock); if (c.getType() != Token.EMPTY) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations extends NodeTraversal.AbstractPostOrderCallback { private final AbstractCompiler compiler; private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); } /** * * In the AST that Rhino gives us, it needs to make a distinction * between jsdoc

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ public void visit(NodeTraversal t, Node n, Node parent) { int nType = n.getType(); switch (nType) { case Token.NAME: case Token.STRING: String nString = n.getString(); if (nType == Token.NAME && n.getParent().getType() == Token.CALL && "eval".equals(nString)) { n.putBooleanProp(Node.DIRECT_EVAL, true); } if (convention.isConstant(nString)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } break; case Token.FUNCTION: JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null) { // Look for the info on other nodes. if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs())

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } break; case Token.OBJECTLIT: if (n.getType() == Token.OBJECTLIT) { for (Node key = n.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); if (key.getJSDocInfo() != null && key.getNext().getType() == Token.FUNCTION) { value.setJSDocInfo(key.getJSDocInfo()); } } } break; } // TODO(johnlenz): Determine if it is possible to simply use the javadoc // everywhere rather than use IS_DISPATCHER. /* * Translate dispatcher info into the property expected node. */ if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) { if (n.getType() == Token.ASSIGN) { Node fnNode = n.getLastChild(); Preconditions.checkState(fnNode.getType() == Token.FUNCTION); fnNode.putBooleanProp(Node.IS_DISPATCHER, true); } } } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> warnings should point. * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType type, JSType castType) { castType = castType.restrictByNotNullOrUndefined(); type = type.restrictByNotNullOrUndefined(); if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) { compiler.report( JSError.make(t, n, INVALID_CAST, castType.toString(), type.toString())); registerMismatch(type, castType); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. */ void expectUndeclaredVariable(String sourceName, Node n, Node parent, Var var, String variableName, JSType newType) { boolean allowDupe = false; if (n.getType() == Token.GETPROP) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitial

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Scope and is a // native type. if (var.input == null) { n.setJSType(varType); if (parent.getType() == Token.VAR) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.getType() == Token.FUNCTION); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().getType() == Token.EXPR_RESULT) || !newType.equals(varType)) { compiler.report( JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } } /** * Expect that all properties on interfaces that this type implements are * implemented. */ void expectAllInterfacePropertiesImplemented(FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { if (!instance.hasProperty(prop)) { Node source = type.getSource(); Preconditions.checkNotNull(source); String sourceName = (String) source.getProp(Node.SOURCENAME_PROP); sourceName = sourceName == null ? "" : sourceName; compiler.report(JSError.make(sourceName, source, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implemented.toString(), instance.toString())); registerMismatch(instance, implemented); } } } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSTypeNative required) { mismatch(t, n, msg, found, getNativeType(required)); } private void mismatch(String sourceName, Node n, String msg, JSType found, JSType required) { registerMismatch(found, required); compiler.report( JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, formatFoundRequired(msg, found, required))); } private void registerMismatch(JSType found, JSType required) { // Don't register a mismatch for differences in null or undefined or if the // code didn't downcast. found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (found.canAssignTo(required) || required.canAssignTo(found)) { return; } mismatches.add(new TypeMismatch(found, required)); if (found instanceof FunctionType && required instanceof FunctionType) { FunctionType fnTypeA = ((FunctionType) found); FunctionType fnTypeB = ((FunctionType) required); Iterator<Node> paramItA = fnTypeA.getParameters().iterator(); Iterator<Node> paramItB = fnTypeB.getParameters().iterator(); while (paramItA.hasNext() && paramItB.hasNext()) { registerIfMismatch(paramItA.next().getJSType(), paramItB.next().getJSType()); } registerIfMismatch(fnTypeA.getReturnType(), fnTypeB.getReturnType()); } } private void registerIfMismatch(JSType found, JSType required) { if (found != null && required != null && !found.canAssignTo(required)) { registerMismatch(found, required); } } /** * Formats a found/required error message. */ private String formatFoundRequired(String description, JSType found, JSType required) { return MessageFormat.format(FOUND_REQUIRED, description, found, required); } /** * Given a node, get a human-readable name for the type of that node so * that will be easy for the programmer to find the original declaration. * * For example, if SubFoo's property "

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>bar" might have the human-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.getType() == Token.GETPROP) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type instanceof FunctionType) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set<String> unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation List<Name> allDefines = Lists.newArrayList(); for (Name name : namespace.getNameIndex().values()) { if (name.docInfo != null && name.docInfo.isDefine()) { allDefines.add(name); } else if (name.refs != null) { for (Ref ref : name.refs) { Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.getType() == Token.VAR && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, all

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.getType() == Token.ASSIGN) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.getType() == Token.NAME); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.getType() == Token.CALL) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, expecially because // the intended use of defines is with config_files, where // all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element()

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( JSError.make(t, value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( JSError.make(t, valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info); assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( JSError.make(t, valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().getType() == Token.VAR ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. * */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb;

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> if (cb instanceof ScopedCallback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (!sourceName.isEmpty()) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false, false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { sourceName = ""; curNode = root; pushScope(root); traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.getType() == Token.FUNCTION) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(sourceName); } /** * Gets the current input module. */ public JSModule getModule() { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block traverseBranch(n.getFirstChild(), n); traverseBranch(n.getFirstChild().getNext().getNext(), n); break; case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node fnName = n.getFirstChild(); boolean anonymous = parent != null && NodeUtil.isFunctionAnonymous(n); if (!anonymous) { // Named functions are parent of the containing scope. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (anonymous) { // Anonymous function names are parent of the contained scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>modules.length]; for (int i = 0; i < modules.length; i++) { expected[i] = ""; for (CompilerInput input : modules[i].getInputs()) { expected[i] += input.getSourceFile().getCode(); } } test(modules, expected, null, warning); } catch (IOException e) { throw new RuntimeException(e); } } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ protected void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning) { test(compiler, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ private void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { RecentChange recentChange = new RecentChange(); compiler.addChangeHandler(recentChange); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = root.getLastChild

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> assertEquals( "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); // Verify the symbol table. ErrorManager symbolTableErrorManager = new BlackHoleErrorManager(compiler); Node expectedRoot = parseExpectedJs(expected); expectedRoot.detachFromParent(); SymbolTable table = compiler.acquireSymbolTable(); table.verify( new Node(Token.BLOCK, externsRoot.cloneTree(), expectedRoot), mainRoot.getParent()); table.release(); JSError[] stErrors = symbolTableErrorManager.getErrors(); if (expectedSymbolTableError != null) { assertEquals("There should be one error.", 1, stErrors.length); assertEquals(expectedSymbolTableError, stErrors[0].getType()); } else { assertEquals("Unexpected symbol table error(s): " + Joiner.on("\n").join(stErrors), 0, stErrors.length); } if (warning == null) { assertEquals( "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), 0, aggregateWarningCount); } else { assertEquals("There should be one warning, repeated " + numRepetitions + " time(s).", numRepetitions, aggregateWarningCount); for (int i = 0; i < numRepetitions; ++i) { JSError[] warnings = errorManagers[i].getWarnings(); JSError actual = warnings[0]; assertEquals(warning, actual.getType()); // Make sure that source information is always provided. if (!allowSourcelessWarnings) { assertTrue("Missing source file name in warning", actual.sourceName != null && !actual.sourceName.isEmpty()); assertTrue("Missing line number in warning", -1 != actual.lineNumber); assertTrue("Missing char number in warning", -1 != actual.getCharno()); } if (description != null) { assertEquals(description, actual.description); } } } if (normalizeEnabled) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRootClone, mainRootClone); } if (mainRootClone.checkTreeEqualsSilent(mainRoot)) { assertFalse(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> = p2.error.sourceName; if (source1 != null && source2 != null) { int sourceCompare = source1.compareTo(source2); if (sourceCompare != 0) { return sourceCompare; } } else if (source1 == null && source2 != null) { return P1_LT_P2; } else if (source1 != null && source2 == null) { return P1_GT_P2; } // lineno comparison int lineno1 = p1.error.lineNumber; int lineno2 = p2.error.lineNumber; if (lineno1 != lineno2) { return lineno1 - lineno2; } else if (lineno1 < 0 && 0 <= lineno2) { return P1_LT_P2; } else if (0 <= lineno1 && lineno2 < 0) { return P1_GT_P2; } // charno comparison int charno1 = p1.error.getCharno(); int charno2 = p2.error.getCharno(); if (charno1 != charno2) { return charno1 - charno2; } else if (charno1 < 0 && 0 <= charno2) { return P1_LT_P2; } else if (0 <= charno1 && charno2 < 0) { return P1_GT_P2; } // description return p1.error.description.compareTo(p2.error.description); } } static class ErrorWithLevel { final JSError error; final CheckLevel level; ErrorWithLevel(JSError error, CheckLevel level) { this.error = error; this.level = level; } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType INVALID_FUNCTION_DECL = DiagnosticType.error("JSC_INVALID_FUNCTION_DECL", "Syntax error: function declaration must have a name"); private CompilerInput synthesizedExternsInput = null; private Node synthesizedExternsRoot = null; private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.sanityCheck = sanityCheck; } /** {@inheritDoc} */ public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } /** {@inheritDoc} */ public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.NAME) { return; } if (NodeUtil.isLabelName(n)) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(NodeUtil.isFunction(parent)); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isAnonymousFunction(parent)) { t.report(n, INVALID_FUNCTION_DECL); } return; } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isAnonymousFunction(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { t.report(n, UNDEFINED_VAR_ERROR, varName); if

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { // Create a new variable in a synthetic script. This will prevent // subsequent compiler passes from crashing. Node nameNode = Node.newString(Token.NAME, varName); getSynthesizedExternsRoot().addChildToBack( new Node(Token.VAR, nameNode)); scope.getGlobalScope().declare(varName, nameNode, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (!sanityCheck && scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { public void visit(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.GETPROP: case Token.LP: // These are okay. break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = compiler.newExternInput("{SyntheticVarsDeclar}"); } return synthesizedExternsInput; } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) { CompilerInput synthesizedExterns = getSynthesizedExternsInput(); synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); } return synthesizedExternsRoot; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>/* * Copyright 2005 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A JavaScript module has a unique name, consists of a list of compiler inputs, * and can depend on other modules. * * * */ public class JSModule { /** Module name */ private final String name; /** Source code inputs */ private final List<CompilerInput> inputs = new ArrayList<CompilerInput>(); /** Modules that this module depends on */ private final List<JSModule> deps = new ArrayList<JSModule>(); /** * Creates an instance. * * @param name A unique name for the module */ public JSModule(String name) { this.name = name; } /** Gets the module name. */ public String getName() { return name; } /** Adds a source file input to this module. */ public void add(JSSourceFile file) { add(new CompilerInput(file)); } /** Adds a source file input to this module. */ public void addFirst(JSSourceFile file) { addFirst(new CompilerInput(file)); } /** Adds a source code input to this module. */ public void add(CompilerInput input) {

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> inputs.add(input); input.setModule(this); } /** Adds a source code input to this module. */ public void addFirst(CompilerInput input) { inputs.add(0, input); input.setModule(this); } /** Adds a source code input to this module directly after other. */ public void addAfter(CompilerInput input, CompilerInput other) { Preconditions.checkState(inputs.contains(other)); inputs.add(inputs.indexOf(other), input); input.setModule(this); } /** Adds a dependency on another module. */ public void addDependency(JSModule dep) { Preconditions.checkState(dep != this); deps.add(dep); } /** Removes all of the inputs from this module. */ public void removeAll() { for (CompilerInput input : inputs) { input.setModule(null); } inputs.clear(); } /** * Gets the list of modules that this module depends on. * * @return A list that may be empty but not null */ public List<JSModule> getDependencies() { return deps; } /** * Returns the transitive closure of dependencies starting from the * dependencies of this module. */ public Set<JSModule> getAllDependencies() { Set<JSModule> allDeps = Sets.newHashSet(deps); List<JSModule> workList = Lists.newArrayList(deps); while (workList.size() > 0) { JSModule module = workList.remove(workList.size() - 1); for (JSModule dep : module.getDependencies()) { if (allDeps.add(dep)) { workList.add(dep); } } } return allDeps; } /** Returns this module and all of its dependencies in one list. */ public Set<JSModule> getThisAndAllDependencies() { Set<JSModule> deps = getAllDependencies(); deps.add(this); return deps; } /** * Gets this module's list of source code inputs. * * @return A list that may be empty but not null */ public List<CompilerInput> getInputs() { return inputs; } /** Returns the input with the

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> given name or null if none. */ public CompilerInput getByName(String name) { for (CompilerInput input : inputs) { if (name.equals(input.getName())) { return input; } } return null; } /** * Removes any input with the given name. Returns whether any were removed. */ public boolean removeByName(String name) { boolean found = false; Iterator<CompilerInput> iter = inputs.iterator(); while (iter.hasNext()) { CompilerInput file = iter.next(); if (name.equals(file.getName())) { iter.remove(); file.setModule(null); found = true; } } return found; } /** Returns the module name (primarily for debugging). */ @Override public String toString() { return name; } /** * Removes any references to nodes of the AST. This method is needed to * allow the ASTs to be garbage collected if the modules are kept around. */ public void clearAsts() { for (CompilerInput input : inputs) { input.clearAst(); } } /** * Puts the JS files into a topologically sorted order by their dependencies. */ public void sortInputsByDeps(Compiler compiler) { // Collect all symbols provided in these files. final Map<String, CompilerInput> provides = Maps.newHashMap(); for (CompilerInput input : inputs) { for (String provide : input.getProvides(compiler)) { provides.put(provide, input); } } // Put the files into topologically sorted order by their requires. // NOTE: This will leave the list unchanged if the files are already // topologically sorted. This is important to apps whose dependencies // are incomplete. List<CompilerInput> list = Lists.newArrayList(); Set<CompilerInput> set = Sets.newHashSet(); for (CompilerInput input : inputs) { addInputAndDeps(input, provides, compiler, list, set, Sets.<CompilerInput>newHashSet()); } // Update the JSModule to this order. Preconditions.checkState(inputs.size() == list.size()); inputs.clear(); inputs.addAll(list); } /** *

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Returns the given collection of modules in topological order. * * Note that this will return the modules in the same order if they are * already sorted, and in general, will only change the order as necessary to * satisfy the ordering dependencies. This can be important for cases where * the modules do not properly specify all dependencies. */ public static JSModule[] sortJsModules(Collection<JSModule> modules) { List<JSModule> list = Lists.newArrayList(); Set<JSModule> set = Sets.newHashSet(); for (JSModule module : modules) { addModuleAndDeps(module, list, set, Sets.<JSModule>newHashSet()); } return list.toArray(new JSModule[list.size()]); } /** * Adds the given input and its deps to the given list and set, if they are * not already added, placing dependencies before dependants. */ private static void addInputAndDeps( CompilerInput input, Map<String, CompilerInput> provides, Compiler compiler, List<CompilerInput> list, Set<CompilerInput> set, Set<CompilerInput> inProgress) { if (!set.contains(input)) { if (inProgress.contains(input)) { throw new IllegalArgumentException( "Circular dependency involving input: " + input.getName()); } inProgress.add(input); for (String require : input.getRequires(compiler)) { if (provides.containsKey(require)) { addInputAndDeps(provides.get(require), provides, compiler, list, set, inProgress); } } list.add(input); set.add(input); } } /** * Adds the given module and its deps to the given list and set, if they are * not already added, placing dependencies before dependants. */ private static void addModuleAndDeps( JSModule module, List<JSModule> list, Set<JSModule> set, Set<JSModule> inProgress) { if (!set.contains(module)) { if (inProgress.contains(module)) { throw new IllegalArgumentException( "Circular dependency involving module: " + module.getName()); } inProgress.add(module); for (JSModule dep : module.getDependencies()) {

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(qualifiedName, origType, type); break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor<JSType> restrictUndefinedVisitor = new Visitor<JSType>() { public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().equals(type)) { return enumElementType; } else { return type; } } public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } public JSType caseNoType() { return getNativeType(NO_TYPE); } public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } public JSType caseFunctionType(FunctionType type) { return type; } public JSType caseNullType() { return getNativeType(NULL_TYPE); } public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } public JSType caseObjectType(ObjectType type) { return type; } public JSType caseStringType() { return getNativeType(STRING_TYPE); } public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(VOID_TYPE)); } public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } public JSType caseVoidType() { return null; } }; /** * @see #getRestrictedWithoutNull(JSType) */ private final Visitor<JSType> restrictNullVisitor = new Visitor<JSType>() { public JS

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>JSDocInfo inferJSDocInfo = null; // These fields are used to calculate the percentage of expressions typed. private int typedCount = 0; private int nullCount = 0; private int unknownCount = 0; private boolean inExterns; public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, Scope topScope, ScopeCreator scopeCreator, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.reverseInterpreter = reverseInterpreter; this.typeRegistry = typeRegistry; this.topScope = topScope; this.scopeCreator = scopeCreator; this.reportMissingOverride = reportMissingOverride; this.reportUnknownTypes = reportUnknownTypes; this.inferJSDocInfo = new InferJSDocInfo(compiler); } public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this(compiler, reverseInterpreter, typeRegistry, null, null, reportMissingOverride, reportUnknownTypes); } TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry) { this(compiler, reverseInterpreter, typeRegistry, null, null, CheckLevel.WARNING, CheckLevel.OFF); } /** Turn on the missing property check. Returns this for easy chaining. */ TypeCheck reportMissingProperties(boolean report) { reportMissingProperties = report; return this; } /** * Main entry point for this phase of processing. This follows the pattern for * JSCompiler phases. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ public void process(Node externsRoot, Node jsRoot) { Preconditions.checkNotNull(scopeCreator); Preconditions.checkNotNull(topScope); Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); if (externsRoot

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> != null) { check(externsRoot, true); } check(jsRoot, false); potentialChecks.flush(); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { JSDocInfo info; switch (n.getType()) { case Token.SCRIPT: case Token.VAR: // @notypecheck info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { return false; } break; case Token.FUNCTION: // @notypecheck info = n.getJSDocInfo(); info = (info == null) ? parent.getJSDocInfo() : info; if (info != null && info.isNoTypeCheck()) { return false; } // normal type checking final TypeCheck outerThis = this; final Scope outerScope = t.getScope(); final FunctionType functionType = (FunctionType) n.getJSType(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis());

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> break; case Token.REF_SPECIAL: ensureTyped(t, n); break; case Token.GET_REF: ensureTyped(t, n, getJSType(n.getFirstChild())); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, NUMBER_TYPE); } else { typeable = false; } break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.STRING: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, STRING_TYPE); } else { typeable = false; } break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.getType() == Token.ASSIGN && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID:

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } t.report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: left

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Type = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotVoid( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotVoid( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: if (!isReference(n.getFirstChild())) { t.report(n, BAD_DELETE); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.getType() == Token.OBJECTLIT) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } break; default: t.report(n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { String unresolvedReference = getUnresolvedReference(type); if (unresolvedReference != null) { compiler.report(JSError.make(t, n, reportUnknownTypes, UNRESOLVED_TYPE, unresolvedReference)); } else { compiler.report(JSError.make(t, n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } } unknownCount++; } else { typedCount++; } } /** * Looks through the type to see if it contains an unresolved reference. This * is often the reason that a type is unresolved, and it can occur because of * a simple misspelling of a type name. */ private String getUnresolvedReference(JSType type) { if (type.isNamedType()) { NamedType namedType = (NamedType) type; if (!namedType.is

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Resolved()) { return namedType.getReferenceName(); } } else if (type.isUnionType()) { for (JSType alt : ((UnionType) type).getAlternates()) { if (alt.isUnknownType()) { String unresolvedReference = getUnresolvedReference(alt); if (unresolvedReference != null) { return unresolvedReference; } } } } return null; } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.getType() == Token.GETPROP) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope()), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate(t.getScope())); return; } // object.prototype = ...

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType(); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild);

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>, propertyName, ctorType.getInstanceType().toString())); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isOrdinaryFunction() && !(rvalue.isQualifiedName() && rvalue.getQualifiedName().equals(abstractMethodName))) { compiler.report(JSError.make(t, object, INTERFACE_FUNCTION_MEMBERS_ONLY, abstractMethodName)); } if (assign.getLastChild().getType() == Token.FUNCTION && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report(JSError.make(t, object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits an ASSIGN node for cases such as * <pre> * object.property = ...; * </pre> * that have an {@code @type} annotation. */ private void visitAnnotatedAssignGetprop(NodeTraversal t, Node assign, JSType type, Node object, String property, Node rvalue) { // verifying that the rvalue has the correct type validator.expectCanAssignToPropertyOf(t, assign, getJSType(rvalue), type, object, property); } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.LP || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.getType() == Token.ASSIGN) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotVoid(t, n, childType, "undefined has no properties", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n);

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> return; } checkPropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Make sure that the access of this property is ok. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { ObjectType objectType = childType.dereference(); if (objectType != null) { JSType propType = getJSType(n); if ((!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) && propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { t.report(n, INEXISTENT_ENUM_ELEMENT, propName); } else if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { t.report(n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirstChild(), true)); } } } } else { // TODO(nicksantos): might want to flag the access on a non object when // it's impossible to get a property from this type. } } /** * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; } return false; } /** * Visits a GET

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>ELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } if (info != null && info.hasEnumParameterType()) { // var.getType() can never be null, this would indicate a bug in the // scope creation logic. checkEnumInitializer( t, value, info.getEnumParameterType().evaluate(t.getScope())); } else if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); FunctionType type = getFunctionType(constructor); if (type != null && type.isConstructor()) { visitParameterList(t, n, type); ensureTyped(t, n, type.getInstanceType()); } else { // TODO(user): add support for namespaced objects. if (constructor.getType() != Token.GETPROP) { // TODO(user): make the constructor node have lineno/charno // and use constructor for a more precise error indication. // It seems that GETPROP nodes are missing this information. Node line; if (constructor.getLineno() < 0 || constructor.getCharno() < 0) { line = n; } else { line = constructor; } t.report(line, NOT_A_CONSTRUCTOR); } ensureTyped(t, n); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { JSDocInfo info = n.getJSDocInfo(); FunctionType functionType = (FunctionType) n.getJSType(); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isInterface() || functionType.isConstructor()) { FunctionType baseConstructor = functionType. getPrototype().getImplicitPrototype().getConstructor(); if (baseConstructor != null && baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && (baseConstructor.isConstructor() && functionType.isInterface() || baseConstructor.isInterface() && functionType.isConstructor())) { compiler.report( JSError.make(t, n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> != null) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { t.report(n, BAD_IMPLEMENTED_TYPE, functionPrivateName); } } if (functionType.isConstructor()) { validator.expectAllInterfacePropertiesImplemented(functionType); } } } /** * Visits a CALL node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitCall(NodeTraversal t, Node n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { t.report(n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType instanceof FunctionType) { FunctionType functionType = (FunctionType) childType; // Non-native constructors should never be called directly. if (functionType.isConstructor() && !functionType.isNativeObjectType()) { t.report(n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType.getReturnType()); } else { ensureTyped(t, n); } // TODO: Add something to check for calls of RegExp objects, which is not // supported by IE. Either say something about the return type or warn // about the non-portability of the call or both. } /** * Visits the parameters of a CALL or a NEW node. */ private void visitParameterList(NodeTraversal t, Node call, FunctionType functionType) { Iterator<Node> arguments = call.children().iterator(); arguments.next(); // skip the function name

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Iterator<Node> parameters = functionType.getParameters().iterator(); int ordinal = 0; while (arguments.hasNext() && parameters.hasNext()) { Node parameter = parameters.next(); Node argument = arguments.next(); ordinal++; validator.expectArgumentMatchesParameter(t, argument, getJSType(argument), getJSType(parameter), call, ordinal); } int numArgs = call.getChildCount() - 1; int minArgs = functionType.getMinArguments(); int maxArgs = functionType.getMaxArguments(); if (minArgs > numArgs || maxArgs < numArgs) { t.getCompiler().report( JSError.make(t, call, WRONG_ARGUMENT_COUNT, validator.getReadableJSTypeName(call.getFirstChild(), false), String.valueOf(numArgs), String.valueOf(minArgs), maxArgs != Integer.MAX_VALUE ? " and no more than " + maxArgs + " argument(s)" : "")); } } /** * Visits a RETURN node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); // This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { t.report(left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { t.report(right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>: case Token.ADD: break; default: t.report(n, UNEXPECTED_TOKEN, Node.tokenToName(op)); } ensureTyped(t, n); } /** * <p>Checks the initializer of an enum. An enum can be initialized with an * object literal whose values must be subtypes of the declared enum element * type, or by copying another enum.</p> * * <p>In the case of an enum copy, we verify that the enum element type of the * enum used for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Examples:</p> * <pre>var myEnum = {FOO: ..., BAR: ...}; * var myEnum = myOtherEnum;</pre> * * @param value the value used for initialization of the enum * @param primitiveType The type of each element of the enum. */ private void checkEnumInitializer( NodeTraversal t, Node value, JSType primitiveType) { if (value.getType() == Token.OBJECTLIT) { // re-using value as the value of the object literal and advancing twice value = value.getFirstChild(); value = (value == null) ? null : value.getNext(); while (value != null) { // the value's type must be assignable to the enum's primitive type validator.expectCanAssignTo(t, value, getJSType(value), primitiveType, "element type must match enum's type"); // advancing twice value = value.getNext(); value = (value == null) ? null : value.getNext(); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>t, value, valueEnumPrimitiveType, primitiveType, "incompatible enum element types"); } else { // The error condition is handled in TypedScopeCreator. } } /** * This predicate is used to determine if the node represents an expression * that is a Reference according to JavaScript definitions. * * @param n The node being checked. * @return true if the sub-tree n is a reference, false otherwise. */ private static boolean isReference(Node n) { switch (n.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.NAME: return true; default: return false; } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } /** * Gets the type of the node or {@code null} if the node's type is not a * function. */ private FunctionType getFunctionType(Node n) { JSType type = getJSType(n).restrictByNotNullOrUndefined(); if (type.isUnknownType()) { return typeRegistry.getNativeFunctionType(U2U_CONSTRUCTOR_TYPE); } else if (type instanceof FunctionType) { return (FunctionType) type; } else { return null; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, *

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(n.getType() != Token.FUNCTION || type instanceof FunctionType || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType = info.getType().evaluate(t.getScope()); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n.getType() == Token.GETPROP ? n.getLastChild().getString() : "(missing)"; compiler.report( JSError.make(t, n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> + "\nCached : {0}\nActual : {1}"); static final DiagnosticType SCOPE_MISMATCH = DiagnosticType.error( "JSC_SCOPE_MISMATCH", "Scope roots used with the symbol table do not match." + "\nExpected : {0}\nActual : {1}"); private final AbstractCompiler compiler; private final ScopeCreator scopeCreator; // Mutex so that the symbol table may only be acquired by one pass // at a time. private boolean locked = false; // Memoized data with the pass that has currently acquired the // symbol table. private MemoizedData cache = null; SymbolTable(AbstractCompiler compiler) { this.compiler = compiler; compiler.addChangeHandler(this); scopeCreator = new SyntacticScopeCreator(compiler); } synchronized void acquire() { Preconditions.checkState(!locked, "SymbolTable already acquired"); locked = true; } synchronized void release() { Preconditions.checkState(locked, "SymbolTable already released"); locked = false; } /** * Returns the scope at the given node. */ @Override public Scope createScope(Node n, Scope parent) { // We may only ask for local blocks and the global (all scripts) block. Preconditions.checkArgument( (n.getType() == Token.BLOCK && n.getParent() == null) || n.getType() == Token.FUNCTION, "May only create scopes for the global node and functions"); ensureCacheInitialized(); if (!cache.scopes.containsKey(n)) { cache.scopes.put(n, scopeCreator.createScope(n, parent)); } return cache.scopes.get(n); } /** * Ensure that the memoization data structures have been initialized. */ private void ensureCacheInitialized() { Preconditions.checkState(locked, "Unacquired symbol table"); if (cache == null) { cache = new MemoizedData(); } } /** * If the AST changes, and the symbol table has not been acquired, then * all of our memoized data structures become stale. So delete them. */ @Override public void reportChange() { if (!locked) { cache = null; } } /** * All the

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Preconditions.checkState(expectedScopes.size() == actualScopes.size()); for (int i = 0; i < expectedScopes.size(); i++) { Scope expectedScope = expectedScopes.get(i); Scope actualScope = actualScopes.get(i); if (!checkNodesMatch(expectedScope.getRootNode(), actualScope.getRootNode())) { compiler.report( JSError.make( SCOPE_MISMATCH, expectedScope.getRootNode().toStringTree(), actualScope.getRootNode().toStringTree())); continue; } if (expectedScope.getVarCount() != actualScope.getVarCount()) { compiler.report( JSError.make( VARIABLE_COUNT_MISMATCH, Integer.toString(expectedScope.getVarCount()), Integer.toString(actualScope.getVarCount()))); } else { Iterator<Var> it = expectedScope.getVars(); while (it.hasNext()) { Var var = it.next(); Scope.Var actualVar = actualScope.getVar(var.getName()); if (actualVar == null || expectedScope.getVar(var.getName()) != var) { compiler.report( JSError.make(MISSING_VARIABLE, var.getName())); } else if ( !checkNodesMatch( var.getNameNode(), actualVar.getNameNode()) || !isNodeAttached(actualVar.getNameNode())) { compiler.report( JSError.make(MOVED_VARIABLE, var.getName())); } } } } } /** * Check that the two nodes have the same relative position in the tree. */ private boolean checkNodesMatch(Node nodeA, Node nodeB) { Node currentA = nodeA; Node currentB = nodeB; while (currentA != null && currentB != null) { if (currentA.getType() != currentB.getType() || !currentA.isEquivalentTo(currentB)) { return false; } currentA = currentA.getParent(); currentB = currentB.getParent(); } return currentA == null && currentB == null; } private boolean isNodeAttached(Node node) { // Make sure the cached var is still attached. for (Node current = node; current != null; current

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> = current.getParent()) { if (current.getType() == Token.SCRIPT) { return true; } } return false; } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> except for // their line numbers. Their line numbers will only be exposed for // type name resolution warnings. // // TODO(nicksantos): Change rhino to put the whole Comment object // on the Node. private final Multimap<String, NodeWithJsDoc> nodesWithJsDoc = LinkedHashMultimap.create(); private IRFactory(String sourceString, String sourceName, Config config, ErrorReporter errorReporter) { this.sourceString = sourceString; this.sourceName = sourceName; this.registry = config.registry; this.config = config; this.errorReporter = errorReporter; this.transformDispatcher = new TransformDispatcher(); } public static Node transformTree(AstRoot node, String sourceString, Config config, ErrorReporter errorReporter) { IRFactory irFactory = new IRFactory(sourceString, node.getSourceName(), config, errorReporter); Node irNode = irFactory.transform(node); // @license text gets appended onto the fileLevelJsDocBuilder as found, // and stored straight into the JSDocInfo for the root node. Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = irNode.getJsDocBuilderForNode(); // fileOverviewInfo stores the last bit of fileoverview data we saw. // We only permit one, so throwing away extras is fair. // The fileOverviewInfo gets passed into parseJSDocInfo so that // it can detect when multiple @fileoverviews exist in the same file. JSDocInfo fileOverviewInfo = null; if (node.getComments() != null) { for (Comment comment : node.getComments()) { if (comment.getCommentType() == JSDOC) { JsDocInfoParser jsDocParser = irFactory.createJsDocInfoParser(comment.getValue(), comment.getLineno(), comment.getAbsolutePosition(), fileLevelJsDocBuilder, fileOverviewInfo); if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) { fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo(); } else { JSDocInfo info = jsDocParser.retrieveAndResetParsedJSDocInfo(); if (info != null) { irFactory.attachJs

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Doc(comment, info); } } } } // Only after we've seen all @fileoverview entries, attach the // last one to the root node, and copy the found license strings // to that node. if (fileOverviewInfo != null) { if ((irNode.getJSDocInfo() != null) && (irNode.getJSDocInfo().getLicense() != null)) { fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); } } return irNode; } private Node transform(AstNode node) { String jsDoc = node.getJsDoc(); NodeWithJsDoc nodeWithJsDoc = null; if (jsDoc != null) { nodeWithJsDoc = new NodeWithJsDoc(); nodesWithJsDoc.put(jsDoc, nodeWithJsDoc); } Node irNode = justTransform(node); if (nodeWithJsDoc != null) { nodeWithJsDoc.node = irNode; } // If we have a named function, set the position to that of the name. if (irNode.getType() == Token.FUNCTION && irNode.getFirstChild().getLineno() != -1) { irNode.setLineno(irNode.getFirstChild().getLineno()); irNode.setCharno(irNode.getFirstChild().getCharno()); } else { if (irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); } } return irNode; } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual JSDoc comments and for handling * file-level JSDoc comments (@fileoverview and @license). *

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = new Node(Token.BREAK); if (statementNode.getBreakLabel() != null) { node.addChildToBack(transform(statementNode.getBreakLabel())); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = new Node(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { node.addChildToBack(transform(clauseNode.getCatchCondition())); } else { Node catchCondition = new Node(Token.EMPTY); // Old Rhino used the position of the catchVar as the position // for the (nonexistent) error being caught. catchCondition.setLineno(catchVar.getLineno()); int clauseAbsolutePosition = position2charno(catchVar.getAbsolutePosition()); catchCondition.setCharno(clauseAbsolutePosition); node.addChildToBack(catchCondition); } node.addChildToBack(transform(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return new Node( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = new Node(Token.CONTINUE); if (statementNode.getLabel() != null) { node.addChildToBack(transform(statementNode.getLabel())); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return new Node( Token.DO, transform(loopNode.getBody()), transform(loopNode.getCondition())); } @Override Node processElementGet(ElementGet getNode) { return new Node( Token.GETELEM, transform(getNode.getTarget()), transform(getNode.getElement())); } @Override Node processEmptyExpression(EmptyExpression exprNode) { Node node = new Node

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(Token.EMPTY); return node; } @Override Node processExpressionStatement(ExpressionStatement statementNode) { Node node = new Node(transformTokenType(statementNode.getType())); node.addChildToBack(transform(statementNode.getExpression())); return node; } @Override Node processForInLoop(ForInLoop loopNode) { return new Node( Token.FOR, transform(loopNode.getIterator()), transform(loopNode.getIteratedObject()), transform(loopNode.getBody())); } @Override Node processForLoop(ForLoop loopNode) { Node node = new Node( Token.FOR, transform(loopNode.getInitializer()), transform(loopNode.getCondition()), transform(loopNode.getIncrement())); node.addChildToBack(transform(loopNode.getBody())); return node; } @Override Node processFunctionCall(FunctionCall callNode) { Node node = new Node(transformTokenType(callNode.getType()), transform(callNode.getTarget())); for (AstNode child : callNode.getArguments()) { node.addChildToBack(transform(child)); } int leftParamPos = callNode.getAbsolutePosition() + callNode.getLp(); node.setLineno(callNode.getLineno()); node.setCharno(position2charno(leftParamPos)); return node; } @Override Node processFunctionNode(FunctionNode functionNode) { Name name = functionNode.getFunctionName(); Boolean isUnnamedFunction = false; if (name == null) { name = new Name(); name.setIdentifier(""); isUnnamedFunction = true; } Node node = new com.google.javascript.rhino.FunctionNode( name.getIdentifier()); node.putProp(Node.SOURCENAME_PROP, functionNode.getSourceName()); Node newName = transform(name); if (isUnnamedFunction) { // Old Rhino tagged the empty name node with the line number of the // declaration. newName.setLineno(functionNode.getLineno()); // TODO(user) Mark line number of paren correctly. // Same problem as below - the left paren might not be on the // same line as the function keyword.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> int lpColumn = functionNode.getAbsolutePosition() + functionNode.getLp(); newName.setCharno(position2charno(lpColumn)); } node.addChildToBack(newName); Node lp = new Node(Token.LP); // The left paren's complicated because it's not represented by an // AstNode, so there's nothing that has the actual line number that it // appeared on. We know the paren has to appear on the same line as the // function name (or else a semicolon will be inserted.) If there's no // function name, assume the paren was on the same line as the function. // TODO(user): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLineno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { lp.addChildToBack(transform(param)); } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = new Node(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transform(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transform(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = new Node( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); // Set the line number here so we can fine-tune it in ways transform // doesn't do. n.setLineno(exprNode.getLineno());

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return new Node( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = Node.newString(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); Node node = new Node(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = Node.newString(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = new Node(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { Node n = Node.newString(literalNode.getValue()); return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = new Node(Token.DEFAULT); } else { AstNode expr = caseNode.getExpression(); node = new Node(Token.CASE, transform(expr)); } Node block = new Node(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = new Node(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return new Node(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = new Node(Token.TRY, transform(statementNode.getTryBlock())); Node block = new Node(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if (lineSet == false) { block.setLineno(cc.getLineno()); lineSet = true; } block.addChildToBack(transform(cc)); } node.addChildToBack(block); AstNode finallyBlock = statementNode.getFinallyBlock(); if (finallyBlock != null) { node.addChildToBack(transform(finallyBlock)); } // If we didn't set the line on the catch clause, then // we've got an empty catch clause. Set its line to be the same // as the finally block (to match Old Rhino's behavior.) if ((lineSet == false) && (finallyBlock != null)) { block.setLineno(finallyBlock.getLineno()); } return node; } @Override Node processUnaryExpression(UnaryExpression exprNode) { Node node = new Node(transformTokenType(exprNode.getType()), transform(exprNode.getOperand())); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true); } return node; } @Override Node processVariableDeclaration(VariableDeclaration declarationNode) { Node node = new Node(Token.VAR); for (VariableInitializer child : declarationNode.getVariables()) { node.addChildToBack(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>transform(child)); } return node; } @Override Node processVariableInitializer(VariableInitializer initializerNode) { Node node = transform(initializerNode.getTarget()); if (initializerNode.getInitializer() != null) { node.addChildToBack(transform(initializerNode.getInitializer())); node.setLineno(node.getLineno()); } return node; } @Override Node processWhileLoop(WhileLoop loopNode) { return new Node( Token.WHILE, transform(loopNode.getCondition()), transform(loopNode.getBody())); } @Override Node processWithStatement(WithStatement statementNode) { return new Node( Token.WITH, transform(statementNode.getExpression()), transform(statementNode.getStatement())); } @Override Node processIllegalToken(AstNode node) { errorReporter.error( "Unsupported syntax: " + com.google.javascript.jscomp.mozilla.rhino.Token.typeToName( node.getType()), sourceName, node.getLineno(), "", 0); return new Node(Token.EMPTY); } void reportDestructuringAssign(AstNode node) { errorReporter.error( "destructuring assignment forbidden", sourceName, node.getLineno(), "", 0); } } private static int transformTokenType(int token) { switch (token) { case com.google.javascript.jscomp.mozilla.rhino.Token.ERROR: return Token.ERROR; case com.google.javascript.jscomp.mozilla.rhino.Token.EOF: return Token.EOF; case com.google.javascript.jscomp.mozilla.rhino.Token.EOL: return Token.EOL; case com.google.javascript.jscomp.mozilla.rhino.Token.ENTERWITH: return Token.ENTERWITH; case com.google.javascript.jscomp.mozilla.rhino.Token.LEAVEWITH: return Token.LEAVEWITH; case com.google.javascript.jscomp.mozilla.rhino.Token.RETURN: return Token.RETURN; case com.google.javascript.jscomp.mozilla.rhino.Token.GOTO: return Token.GOTO; case com.google

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>ino.Token.ASSIGN_DIV: return Token.ASSIGN_DIV; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_MOD: return Token.ASSIGN_MOD; case com.google.javascript.jscomp.mozilla.rhino.Token.HOOK: return Token.HOOK; case com.google.javascript.jscomp.mozilla.rhino.Token.COLON: return Token.COLON; case com.google.javascript.jscomp.mozilla.rhino.Token.OR: return Token.OR; case com.google.javascript.jscomp.mozilla.rhino.Token.AND: return Token.AND; case com.google.javascript.jscomp.mozilla.rhino.Token.INC: return Token.INC; case com.google.javascript.jscomp.mozilla.rhino.Token.DEC: return Token.DEC; case com.google.javascript.jscomp.mozilla.rhino.Token.DOT: return Token.DOT; case com.google.javascript.jscomp.mozilla.rhino.Token.FUNCTION: return Token.FUNCTION; case com.google.javascript.jscomp.mozilla.rhino.Token.EXPORT: return Token.EXPORT; case com.google.javascript.jscomp.mozilla.rhino.Token.IMPORT: return Token.IMPORT; case com.google.javascript.jscomp.mozilla.rhino.Token.IF: return Token.IF; case com.google.javascript.jscomp.mozilla.rhino.Token.ELSE: return Token.ELSE; case com.google.javascript.jscomp.mozilla.rhino.Token.SWITCH: return Token.SWITCH; case com.google.javascript.jscomp.mozilla.rhino.Token.CASE: return Token.CASE; case com.google.javascript.jscomp.mozilla.rhino.Token.DEFAULT: return Token.DEFAULT; case com.google.javascript.jscomp.mozilla.rhino.Token.WHILE: return Token.WHILE; case com.google.javascript.jscomp.mozilla.rhino.Token.DO: return Token.DO; case com.google.javascript.jscomp.mozilla.rhino.Token

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.FOR: return Token.FOR; case com.google.javascript.jscomp.mozilla.rhino.Token.BREAK: return Token.BREAK; case com.google.javascript.jscomp.mozilla.rhino.Token.CONTINUE: return Token.CONTINUE; case com.google.javascript.jscomp.mozilla.rhino.Token.VAR: return Token.VAR; case com.google.javascript.jscomp.mozilla.rhino.Token.WITH: return Token.WITH; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH: return Token.CATCH; case com.google.javascript.jscomp.mozilla.rhino.Token.FINALLY: return Token.FINALLY; case com.google.javascript.jscomp.mozilla.rhino.Token.VOID: return Token.VOID; case com.google.javascript.jscomp.mozilla.rhino.Token.RESERVED: return Token.RESERVED; case com.google.javascript.jscomp.mozilla.rhino.Token.EMPTY: return Token.EMPTY; case com.google.javascript.jscomp.mozilla.rhino.Token.BLOCK: return Token.BLOCK; case com.google.javascript.jscomp.mozilla.rhino.Token.LABEL: return Token.LABEL; case com.google.javascript.jscomp.mozilla.rhino.Token.TARGET: return Token.TARGET; case com.google.javascript.jscomp.mozilla.rhino.Token.LOOP: return Token.LOOP; case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_VOID: return Token.EXPR_VOID; case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_RESULT: return Token.EXPR_RESULT; case com.google.javascript.jscomp.mozilla.rhino.Token.JSR: return Token.JSR; case com.google.javascript.jscomp.mozilla.rhino.Token.SCRIPT: return Token.SCRIPT; case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOFNAME: return Token.TYPEOFNAME; case com.google.javascript.jscomp.mozilla.rhino.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> source = getSource(); String sourceExcerpt = source == null ? null : excerpt.get( source, error.sourceName, error.lineNumber, excerptFormatter); // formatting the message StringBuilder b = new StringBuilder(); if (error.sourceName != null) { b.append(error.sourceName); if (error.lineNumber > 0) { b.append(':'); b.append(error.lineNumber); } b.append(": "); } b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR)); b.append(" - "); b.append(error.description); b.append('\n'); if (sourceExcerpt != null) { b.append(sourceExcerpt); b.append('\n'); int charno = error.getCharno(); // padding equal to the excerpt and arrow at the end if (excerpt.equals(LINE) && 0 <= charno && charno < sourceExcerpt.length()) { for (int i = 0; i < charno; i++) { char c = sourceExcerpt.charAt(i); if (Character.isWhitespace(c)) { b.append(c); } else { b.append(' '); } } b.append("^\n"); } } return b.toString(); } /** * Formats a region by appending line numbers in front, e.g. * <pre> 9| if (foo) { * 10| alert('bar'); * 11| }</pre> * and return line excerpt without any modification. */ static class LineNumberingFormatter implements ExcerptFormatter { public String formatLine(String line, int lineNumber) { return line; } public String formatRegion(Region region) { if (region == null) { return null; } String code = region.getSourceExcerpt(); if (code.length() == 0) { return null; } // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> CompilerInput[] getAllInputsFromModules() { List<CompilerInput> inputs = new ArrayList<CompilerInput>(); Map<String, JSModule> inputMap = new HashMap<String, JSModule>(); for (JSModule module : modules_) { for (CompilerInput input : module.getInputs()) { String inputName = input.getName(); JSModule firstModule = inputMap.get(inputName); if (firstModule == null) { inputs.add(input); inputMap.put(inputName, module); } else { report(JSError.make(DUPLICATE_INPUT_IN_MODULES, firstModule.getName(), module.getName(), inputName)); } } } if (hasErrors()) { // There's no reason to bother parsing the code. return new CompilerInput[0]; } return inputs.toArray(new CompilerInput[inputs.size()]); } static final DiagnosticType DUPLICATE_INPUT = DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}"); static final DiagnosticType DUPLICATE_EXTERN_INPUT = DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT", "Duplicate extern input: {0}"); /** * Creates a map to make looking up an input by name fast. Also checks for * duplicate inputs. */ void initInputsByNameMap() { inputsByName_ = new HashMap<String, CompilerInput>(); for (CompilerInput input : externs_) { String name = input.getName(); if (!inputsByName_.containsKey(name)) { inputsByName_.put(name, input); } else { report(JSError.make(DUPLICATE_EXTERN_INPUT, name)); } } for (CompilerInput input : inputs_) { String name = input.getName(); if (!inputsByName_.containsKey(name)) { inputsByName_.put(name, input); } else { report(JSError.make(DUPLICATE_INPUT, name)); } } } public Result compile( JSSourceFile extern, JSSourceFile input, CompilerOptions options) { return compile(extern, new JSSourceFile[] { input }, options); } public Result compile( JSSourceFile extern, JSSourceFile[] input

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>, CompilerOptions options) { return compile(new JSSourceFile[] { extern }, input, options); } public Result compile( JSSourceFile extern, JSModule[] modules, CompilerOptions options) { return compile(new JSSourceFile[] { extern }, modules, options); } /** * Compiles a list of inputs. */ public Result compile(JSSourceFile[] externs, JSSourceFile[] inputs, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { init(externs, inputs, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } /** * Compiles a list of modules. */ public Result compile(JSSourceFile[] externs, JSModule[] modules, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { init(externs, modules, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } private Result compile() { return runInCompilerThread(new Callable<Result>() { public Result call() throws Exception { compileInternal(); return getResult(); } }); } /** * Disable threads. This is for clients that run on AppEngine and * don't have threads. */ public void disableThreads() { useThreads = false; } @SuppressWarnings("unchecked") private <T> T runInCompilerThread(final Callable<T> callable) { // Under JRE 1.6, the jscompiler overflows the stack when running on some // large or complex js code. Here we start a new thread with a larger // stack in order to let the compiler do its thing, without having to // increase the stack size for *every* thread (which is what -Xss does). // Might want to

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> } SourceAst ast = new SyntheticAst(name); CompilerInput input = new CompilerInput(ast, name, true); inputsByName_.put(name, input); externsRoot.addChildToFront(ast.getAstRoot(this)); return input; } /** Add a source input dynamically. Intended for incremental compilation. */ void addIncrementalSourceAst(JsAst ast) { String sourceName = ast.getSourceFile().getName(); Preconditions.checkState( getInput(sourceName) == null, "Duplicate input of name " + sourceName); inputsByName_.put(sourceName, new CompilerInput(ast)); } @Override JSModuleGraph getModuleGraph() { return moduleGraph_; } @Override public JSTypeRegistry getTypeRegistry() { if (typeRegistry == null) { typeRegistry = new JSTypeRegistry(oldErrorReporter); } return typeRegistry; } @Override ScopeCreator getScopeCreator() { return getPassConfig().getScopeCreator(); } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options_.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options_.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main js sources.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> jsRoot = new Node(Token.BLOCK); jsRoot.setIsSyntheticBlock(true); if (options_.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options_.tracer == TracerMode.ALL); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer("parseInputs"); try { // Parse externs sources. externsRoot = new Node(Token.BLOCK); externsRoot.setIsSyntheticBlock(true); for (CompilerInput input : externs_) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } for (CompilerInput input : inputs_) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } // Inputs can have a null AST during initial parse. if (n == null) { continue; } if (devMode) { runSanityCheck(); if (hasErrors()) { return null; } } if (options_.sourceMapOutputPath != null || options_.nameReferenceReportPath != null) { // Annotate the nodes in the tree with information from the // input file. This information is used to construct the SourceMap. SourceInformationAnnotator sia = new SourceInformationAnnotator(input.getName()); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } externAndJsRoot = new Node(Token.BLOCK, externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); return externAndJsRoot; } finally { stopTracer(tracer, "parseInputs"); } } public Node parse(JSSourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [synthetic] ", js)); inputsByName_.put(input.getName(), input); return input.getAstRoot

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(this); } void initCompilerOptionsIfTesting() { if (options_ == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(new CompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(JSSourceFile.fromCode(fileName, js)); } Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsByName_ == null) { inputsByName_ = Maps.newHashMap(); } inputsByName_.put(input.getName(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to js code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode = jsRoot.getFirstChild(); scriptNode != null; scriptNode = scriptNode.getNext()) { toSource(cb, i++, scriptNode); } } return cb.toString(); } finally { stopTracer(tracer, "toSource"); } } }); } /** * Converts the parse tree for each input back to js code. */ public String[] toSourceArray() { return runInCompilerThread(new Callable<String[]>() { public String[] call() throws Exception { Tracer tracer = newTracer("toSourceArray"); try { int numInputs = inputs_.length; String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs_[i].getAstRoot(Compiler.this); cb.reset(); toSource(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(); } @Override boolean hasHaltingErrors() { return !isIdeMode() && getErrorCount() > 0; } /** * Consults the {@link ErrorManager} to see if we've encountered errors * that should halt compilation. <p> * * If {@link CompilerOptions#ideMode} is {@code true}, this function * always returns {@code false} without consulting the error manager. The * error manager will continue to be told about new errors and warnings, but * the compiler will complete compilation of all inputs.<p> */ public boolean hasErrors() { return hasHaltingErrors(); } /** Called from the compiler passes, adds debug info */ @Override void addToDebugLog(String str) { debugLog_.append(str); debugLog_.append('\n'); logger_.fine(str); } private SourceFile getSourceFileByName(String sourceName) { if (inputsByName_.containsKey(sourceName)) { return inputsByName_.get(sourceName).getSourceFile(); } return null; } public String getSourceLine(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getLine(lineNumber); } return null; } public Region getSourceRegion(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getRegion(lineNumber); } return null; } //------------------------------------------------------------------------ // Package-private helpers //------------------------------------------------------------------------ @Override Node getNodeForCodeInsertion(JSModule module) { if (module == null) { if (inputs_.length == 0) { throw new IllegalStateException("No inputs"); } return inputs_[0].getAstRoot(this); } List<CompilerInput> inputs = module.getInputs(); if (inputs.size() > 0) { return inputs.get(0).getAstRoot(this); } for (JSModule m : getModuleGraph().getTransitiveDepsDeepestFirst

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionAnonymous(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionAnonymous(n)) { return; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext().getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>; } boolean allowDupe = false; JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); if (!allowDupe) { compiler.report( JSError.make(sourceName, nodeWithLineNumber, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } } } /** * Declares a variable. * * @param name The variable name * @param n The node corresponding to the variable name (usually a NAME node) * @param parent The parent node of {@code n} * @param gramps The parent node of {@code parent} * @param declaredType The variable's type, according to JSDoc * @param nodeWithLineNumber The node to use to access the line number of * the variable declaration, if needed */ private void declareVar(String name, Node n, Node parent, Node gramps, JSType declaredType, Node nodeWithLineNumber) { if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, parent, gramps, nodeWithLineNumber); } else { scope.declare(name, n, declaredType, compiler.getInput(sourceName)); } } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import java.nio.charset.Charset; /** * A code generator that outputs type annotations for functions and * constructors. * */ class TypedCodeGenerator extends CodeGenerator { TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) { super(consumer, outputCharset, true); } @Override void add(Node n, Context context) { Node parent = n.getParent(); if (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT) { if (n.getType() == Token.FUNCTION) { add(getFunctionAnnotation(n)); } else if (n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN) { Node rhs = n.getFirstChild().getFirstChild(); add(getTypeAnnotation(rhs)); } else if (n.getType() == Token.VAR && n.getFirstChild().getFirstChild() != null && n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) { add(getFunctionAnnotation(n.getFirstChild().getFirstChild())); } } super.add(n, context); } private String getTypeAnnotation(Node node) { JSType type = node.getJSType();

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> if (type instanceof FunctionType) { return getFunctionAnnotation(node); } else if (type != null && !type.isUnknownType() && !type.isEmptyType() && !type.isVoidType()) { return "/** @type {" + node.getJSType() + "} */\n"; } else { return ""; } } /** * @param node A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node node) { StringBuilder sb = new StringBuilder("/**\n"); if (node.getJSType().isUnknownType()) { return ""; } FunctionType funType = (FunctionType) node.getJSType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 Node fnNode = funType.getSource(); if (fnNode != null) { Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * @param {" + n.getJSType() + "} "); sb.append(paramNode.getString()); sb.append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * @return {" + retType + "}\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType(); if (!superInstance.toString().equals("Object")) { sb.append(" * @extends {" + superInstance + "}\n"); } for (ObjectType interfaze : funType.getImplementedInterfaces()) { sb.append(" * @implements {" + interfaze + "}\n"); }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> { this.rootRenamer = new ContextualRenamer(); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used the starting context can not // be a function. Preconditions.checkState( declarationRoot.getType() != Token.FUNCTION || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (declarationRoot.getType() == Token.FUNCTION) { // Add the function parameters Node fnParams = declarationRoot.getFirstChild().getNext(); for (Node c = fnParams.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = declarationRoot.getLastChild(); findDeclaredNames(functionBody, null, renamer); } else { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null && !NodeUtil.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>isFunctionDeclaration(n)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove function recursive name (if any). nameStack.pop(); break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> /** * Prepare a set for the new scope. */ public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } referenceStack.push(referencedNames); referencedNames = Sets.newHashSet(); } /** * Rename vars for the current scope, and merge any referenced * names into the parent scope reference set. */ public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); handleScopeVar(v); } // Merge any names that were referenced but not declared in the current // scope. Set<String> current = referencedNames; referencedNames = referenceStack.pop(); // If there isn't anything left in the stack we will be going into the // global scope: don't try to build a set of referenced names for the // global scope. if (!referenceStack.isEmpty()) { referencedNames.addAll(current); } } /** * For the Var declared in the current scope determine if it is possible * to revert the name to its orginal form without conflicting with other * values. */ void handleScopeVar(Var v) { String name = v.getName(); if (containsSeparator(name)) { String newName = getOrginalName(name); // Check if the new name is valid and if it would cause conflicts. if (TokenStream.isJSIdentifier(newName) && !referencedNames.contains(newName) && !newName.equals(ARGUMENTS)) { referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.getType() == Token.NAME); n.setString(newName); } compiler.reportCodeChange(); } nameMap.remove(name); } } @Override public

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> private JSType currentClass = null; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); } public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.getType() == Token.ASSIGN) { Node lValue = parent.getFirstChild(); if (lValue.isQualifiedName()) { if (lValue.getType() == Token.GETPROP) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return ((FunctionType) lValueType).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> } else if (NodeUtil.isFunctionDeclaration(n) || parent.getType() == Token.NAME) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isConstructor()) { return ((FunctionType) type).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( JSError.make(t, n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( JSError.make(t, n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR || parent.getType() == Token.NEW) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( JSError.make(t, n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( JSError.make(t, n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.getType() == Token.NEW) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( JSError.make(t, n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( JSError.make(t, n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE && !t.getInput().getName().equals(docInfo.getSourceName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( JSError.make(t, name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), docInfo.getSourceName())); } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = t.inGlobalScope() && parent.getType() == Token.ASSIGN && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } boolean sameInput = t.getInput().getName().equals(docInfo.getSourceName()); Visibility visibility = docInfo.getVisibility(); JSType owner

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #2 (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || // Case #3 (scopeRootParent != null && scopeRootParent.getType() == Token.ASSIGN && getTypeDeprecationInfo( getClassOfMethod(scopeRoot, scopeRootParent)) != null); } /** * Returns whether this is a function node annotated as deprecated. */ private static boolean isDeprecatedFunction(Node n, Node parent) { if (n.getType() == Token.FUNCTION) { JSType type = n.getJSType(); if (type != null) { return getTypeDeprecationInfo(type) != null; } } return false; } /** * Returns the deprecation reason for the type if it is marked * as being deprecated. Returns empty string if the type is deprecated * but no reason was given. Returns null if the type is not deprecated. */ private static String getTypeDeprecationInfo(JSType type) { if (type == null) { return null; } JSDocInfo info = type.getJSDocInfo(); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType objType = ObjectType.cast(type); if (objType != null) { ObjectType implicitProto = objType.getImplicitPrototype(); if (implicitProto != null) { return getTypeDeprecationInfo(implicitProto); } } return null; } /** * Returns the deprecation reason for the property if it is marked * as being deprecated. Returns empty string if the property is deprecated * but no reason was given. Returns null if the property is not deprecated. */ private static String getPropertyDeprecationInfo(ObjectType type, String prop) { JSDocInfo info = type.getOwnPropertyJSDocInfo(prop); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType implicitProto = type.getImplicitPrototype(); if

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>> callbacks) { Preconditions.checkArgument(callbacks.size() > 0); Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); return new CombinedCompilerPass(compiler, array); } /** A compiler pass that resolves types in the global scope. */ private class GlobalTypeResolver implements CompilerPass { private final AbstractCompiler compiler; GlobalTypeResolver(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (topScope == null) { typedScopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = typedScopeCreator.createScope(root.getParent(), null); } else { compiler.getTypeRegistry().resolveTypesInScope(topScope); } } } /** Checks global name usage. */ private final PassFactory checkGlobalNames = new PassFactory("Check names", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Create a global namespace for analysis by check passes. // Note that this class does all heavy computation lazily, // so it's OK to create it here. namespaceForChecks = new GlobalNamespace(compiler, jsRoot); new CheckGlobalNames(compiler, options.checkGlobalNamesLevel) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Checks for properties that are not read or written */ private final PassFactory checkSuspiciousProperties = new PassFactory("checkSuspiciousProperties", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new SuspiciousPropertiesCheck( compiler, options.checkUndefinedProperties, options.checkUnusedPropertiesEarly ? CheckLevel.WARNING : CheckLevel.OFF); } }; /** Checks that the code is ES5 or Caja compliant. */ private final PassFactory checkStrictMode = new PassFactory("checkStrictMode", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new StrictModeCheck(compiler, !options.checkSymbols, // don't check

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> FunctionParamBuilder(JSTypeRegistry registry) { this.registry = registry; } /** * Add parameters of the given type to the end of the param list. * @return False if this is called after optional params are added. */ public boolean addRequiredParams(JSType ...types) { if (hasOptionalOrVarArgs()) { return false; } for (JSType type : types) { newParameter(type); } return true; } /** * Add optional parameters of the given type to the end of the param list. * @param types Types for each optional parameter. The builder will make them * undefineable. * @return False if this is called after var args are added. */ public boolean addOptionalParams(JSType ...types) { if (hasVarArgs()) { return false; } for (JSType type : types) { newParameter(registry.createOptionalType(type)).setOptionalArg(true); } return true; } /** * Add variable arguments to the end of the parameter list. * @return False if this is called after var args are added. */ public boolean addVarArgs(JSType type) { if (hasVarArgs()) { return false; } // There are two types of variable argument functions: // 1) Programmer-defined var args // 2) Native bottom types that can accept any argument. // For the first one, "undefined" is a valid value for all arguments. // For the second, we do not want to cast it up to undefined. if (!type.isEmptyType()) { type = registry.createOptionalType(type); } newParameter(type).setVarArgs(true); return true; } /** * Copies the parameter specification from the given node. */ public void newParameterFromNode(Node n) { Node newParam = newParameter(n.getJSType()); newParam.setVarArgs(n.isVarArgs()); newParam.setOptionalArg(n.isOptionalArg()); } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, "");

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>"; if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; if (d == 0.0) return "0"; if ((base < 2) || (base > 36)) { throw Context.reportRuntimeError1( "msg.bad.radix", Integer.toString(base)); } if (base != 10) { return DToA.JS_dtobasestr(base, d); } else { StringBuffer result = new StringBuffer(); DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d); return result.toString(); } } /** * If str is a decimal presentation of Uint32 value, return it as long. * Othewise return -1L; */ public static long testUint32String(String str) { // The length of the decimal string representation of // UINT32_MAX_VALUE, 4294967296 final int MAX_VALUE_LENGTH = 10; int len = str.length(); if (1 <= len && len <= MAX_VALUE_LENGTH) { int c = str.charAt(0); c -= '0'; if (c == 0) { // Note that 00,01 etc. are not valid Uint32 presentations return (len == 1) ? 0L : -1L; } if (1 <= c && c <= 9) { long v = c; for (int i = 1; i != len; ++i) { c = str.charAt(i) - '0'; if (!(0 <= c && c <= 9)) { return -1; } v = 10 * v + c; } // Check for overflow if ((v >>> 32) == 0) { return v; } } } return -1; } static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } // ------------------ // Statements // ------------------ public static String getMessage0(String messageId) { return getMessage(messageId, null);

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.List; import java.util.Map; /** * CodingConvention defines a set of hooks to customize the behavior of the * Compiler for a specific team/company. * * * */ public class DefaultCodingConvention implements CodingConvention { @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return !isVarArgsParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return parameter.getParent().getLastChild() == parameter; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return false; } @Override public SubclassRelationship getClassesDefinedBy

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Name The source file name * @param lineno Line number with source file, or -1 if unknown * @param charno Column number within line, or -1 for whole line. * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, int lineno, int charno, DiagnosticType type, String... arguments) { return new JSError(sourceName, lineno, charno, type, null, arguments); } /** * Creates a JSError at a given source location * * @param sourceName The source file name * @param lineno Line number with source file, or -1 if unknown * @param charno Column number within line, or -1 for whole line. * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, int lineno, int charno, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError(sourceName, lineno, charno, type, level, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, DiagnosticType type, String... arguments) { return new JSError(sourceName, n, type, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError(sourceName, n.getLineno(), n.getCharno(), type, level, arguments); } /** * Creates a JSError during NodeTraversal. * * @param t Determines source file name containing current script * @param

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(NodeTraversal t, Node n, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError(t.getSourceName(), n.getLineno(), n.getCharno(), type, level, arguments); } /** * Creates a JSError during NodeTraversal. * * @param t Determines source file name containing current script * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(NodeTraversal t, Node n, DiagnosticType type, String... arguments) { return new JSError(t.getSourceName(), n, type, arguments); } // // JSError constructors // /** * Creates a JSError at a CheckLevel for a source file location. Package * private to avoid any entanglement with code outside of the compiler. * * This is a preferred internal constructor. */ private JSError(String sourceName, int lineno, int charno, DiagnosticType type, CheckLevel level, String... arguments) { this.type = type; this.description = type.format.format(arguments); this.lineNumber = lineno; this.charno = charno; this.sourceName = sourceName; this.level = level == null ? type.level : level; } /** * Creates a JSError for a source file location. Package private to avoid * any entanglement with code outside of the compiler. * * This is a preferred internal constructor. */ private JSError(String sourceName, Node node, DiagnosticType type, String... arguments) { this(sourceName, (node != null) ? node.getLineno() : -1, (node != null) ? node.getCharno() : -1, type, null, arguments); } public DiagnosticType getType() { return type; } /** * Format a message at the given level. * * @return the formatted message or {@code null

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>} */ public String format(CheckLevel level, MessageFormatter formatter) { switch (level) { case ERROR: return formatter.formatError(this); case WARNING: return formatter.formatWarning(this); default: return null; } } @Override public String toString() { // TODO(user): remove custom toString. return type.key + ". " + description + " at " + (sourceName != null && sourceName.length() > 0 ? sourceName : "(unknown source)") + " line " + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)"); } /** * Get the character number. */ public int getCharno() { return charno; } @Override public boolean equals(Object o) { // Generated by Intellij IDEA if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JSError jsError = (JSError) o; if (charno != jsError.charno) { return false; } if (lineNumber != jsError.lineNumber) { return false; } if (!description.equals(jsError.description)) { return false; } if (level != jsError.level) { return false; } if (sourceName != null ? !sourceName.equals(jsError.sourceName) : jsError.sourceName != null) { return false; } if (!type.equals(jsError.type)) { return false; } return true; } @Override public int hashCode() { // Generated by Intellij IDEA int result = type.hashCode(); result = 31 * result + description.hashCode(); result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0); result = 31 * result + lineNumber; result = 31 * result + level.hashCode(); result = 31 * result + charno; return result; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>) { return processBlock((Block) node); } else if (node instanceof Scope) { return processScope((Scope) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.BREAK: return processBreakStatement((BreakStatement) node); case Token.CALL: return processFunctionCall((FunctionCall) node); case Token.CASE: case Token.DEFAULT: return processSwitchCase((SwitchCase) node); case Token.CATCH: case Token.FINALLY: return processCatchClause((CatchClause) node); case Token.COLON: return processObjectProperty((ObjectProperty) node); case Token.CONTINUE: return processContinueStatement((ContinueStatement) node); case Token.DO: return processDoLoop((DoLoop) node); case Token.EMPTY: return processEmptyExpression((EmptyExpression) node); case Token.EXPR_RESULT: case Token.EXPR_VOID: if (node instanceof ExpressionStatement) { return processExpressionStatement((ExpressionStatement) node); } else if (node instanceof LabeledStatement) { return processLabeledStatement((LabeledStatement) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.DEBUGGER: case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: return processKeywordLiteral((KeywordLiteral) node); case Token.FOR: if (node instanceof ForInLoop) { return processForInLoop((ForInLoop) node); } else if (node instanceof ForLoop) { return processForLoop((ForLoop) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.FUNCTION: return processFunctionNode((FunctionNode) node); case Token.GETELEM: return processElementGet((ElementGet) node); case Token.GETPROP: return processPropertyGet

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.LP: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) return true; break; case Token.FUNCTION: // Anonymous functions don't have side-effects, but named ones // change the namespace. Therefore, we check if the function has // a name. Either way, we don't need to check the children, since // they aren't executed at declaration time. // return !isFunctionAnonymous(n); case Token.NEW: { if (checkForNewObjects) { return true; } // calls to constructors that have no side effects have the // no side effect property set. if (n.isNoSideEffectsCall()) { break; } // certain constructors are certified side effect free Node constructor = n.getFirstChild(); if (Token.NAME == constructor.getType()) { String className = constructor.getString(); if (CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(className)) { // loop below will see if the constructor parameters have // side-effects break; } } else { // the constructor could also be an expression like // new (useArray ? Object : Array)(); } } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (n.isNoSide

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>EffectsCall()) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) break; if (isAssignmentOp(n)) { // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects)) { return true; } Node current = n.getFirstChild(); for (; current.getType() == Token.GETPROP || current.getType() == Token.GETELEM; current = current.getFirstChild()) { } return !(isLiteralValue(current) || current.getType() == Token.FUNCTION); } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.NEW, "Expected NEW node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.getType() == Token.NAME && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.CALL, "Expected CALL node, got " + Token.name(callNode.getType())); if (

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.ARRAYLIT: case Token.CALL: case Token.EMPTY: case Token.FALSE: case Token.FUNCTION: case Token.GETELEM: case Token.GETPROP: case Token.GET_REF: case Token.IF: case Token.LP: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.RETURN: case Token.STRING: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Node.tokenToName(type) + " (type " + type + ")"); } } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatentation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assiment op"); } static boolean isExpressionNode(Node n) { return n.getType() == Token.EXPR_RESULT; } /** * Determines if the given node contains a function declaration. */ static boolean containsFunctionDeclaration(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the subtree contains references to 'this' keyword */ static boolean referencesThis(Node n) { return containsType(n, Token.THIS); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.getType() == Token.GETPROP || n.getType() == Token.GETELEM; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> /** * Is this a GETPROP node? */ static boolean isGetProp(Node n) { return n.getType() == Token.GETPROP; } /** * Is this a NAME node? */ static boolean isName(Node n) { return n.getType() == Token.NAME; } /** * Is this a NEW node? */ static boolean isNew(Node n) { return n.getType() == Token.NEW; } /** * Is this a VAR node? */ static boolean isVar(Node n) { return n.getType() == Token.VAR; } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.getType() == Token.NAME && n.getParent().getType() == Token.VAR; } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(isName(n)); Node parent = n.getParent(); if (isVar(parent)) { return n.getFirstChild(); } else if (isAssign(parent) && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this a STRING node? */ static boolean isString(Node n) { return n.getType() == Token.STRING; } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN; } /** * Is this an ASSIGN node? */ static boolean isAssign(Node

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> n) { return n.getType() == Token.ASSIGN; } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.CALL; } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.getType() == Token.FOR && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild(); case Token.DO: return n.getFirstChild(); default: return null; } } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node, for instance, // is either part of an expression (as a anonymous function) or as // a statement.

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.getType() == Token.CASE || n.getType() == Token.DEFAULT; } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty anonymous function name). */ static boolean isReferenceName(Node n) { return isName(n) && !n.getString().isEmpty() && !isLabelName(n); } /** @return Whether the node is a label name. */ static boolean isLabelName(Node n) { if (n != null && n.getType() == Token.NAME) { Node parent = n.getParent(); switch (parent.getType()) { case Token.LABEL: case Token.BREAK: case Token.CONTINUE: if (n == parent.getFirstChild()) { return true; } } } return false; } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.getType() == Token.TRY && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { // Node parent = node.getParent(); if (isStatementBlock(parent) || isSwitchCase(node) || isTryFinallyNode(parent, node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (node.getType

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>() == Token.BLOCK) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (parent.getType() == Token.LABEL && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.getType() == Token.FOR && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, new Node(Token.EMPTY)); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.getType() == Token.BLOCK); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (NodeUtil.isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else if (parent.getType() == Token.LABEL && block.hasOneChild()) { parent.replaceChild(block, block.removeFirstChild()); return true; } else { return false; } } /** * Is this a CALL node? */ static boolean isCall(Node n) { return n.getType() == Token.CALL; } /** * Is this a FUNCTION node? */ static boolean isFunction(Node n) { return n.getType() == Token.FUNCTION; } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunction

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Body(Node fn) { Preconditions.checkArgument(isFunction(fn)); return fn.getLastChild(); } /** * Is this a THIS node? */ static boolean isThis(Node node) { return node.getType() == Token.THIS; } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not anonymous; see {@link #isFunctionAnonymous}). */ static boolean isFunctionDeclaration(Node n) { return n.getType() == Token.FUNCTION && !isFunctionAnonymous(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return NodeUtil.isFunctionDeclaration(n) && (n.getParent().getType() == Token.SCRIPT || n.getParent().getParent().getType() == Token.FUNCTION); } /** * Is this node an anonymous function? An anonymous function is one that has * either no name or a name that is not added to the current scope (see * {@link #isFunctionAnonymous}). */ static boolean isAnonymousFunction(Node n) { return n.getType() == Token.FUNCTION && isFunctionAnonymous(n); } /** * Is a FUNCTION node an anonymous function? An anonymous function is one that * has either no name or a name that is not added to the current scope. * * <p>Some examples of anonymous functions: * <pre> * function () {} * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> anonymous: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> * </pre> * * @param n A FUNCTION node * @return Whether n is an anonymous function */ static boolean isFunctionAnonymous(Node n) { return !isStatement(n); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(isFunction(function)); return NodeUtil.isNameReferenced( function.getLastChild(), "arguments", Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION))); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.getType() == Token.CALL) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (NodeUtil.isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.getType() == Token.STRING) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true;

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(Token.FUNCTION))); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString())); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.getType() == Token

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode(String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(name, -1, -1); setDebugInformation(node, basisNode, originalName); return node; } /** * Sets the debug information (source file info and orignal name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copyInformationFromForTree(basisNode); node.putProp(Node.ORIGINALNAME_PROP, originalName); } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * * @return The node created. */ static Node newName(String name, Node basisNode) { Node nameNode = Node.newString(Token.NAME, name); nameNode.copyInformationFrom(basisNode); return nameNode; } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return The node

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> created. */ static Node newName(String name, Node basisNode, String originalName) { Node nameNode = newName(name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. NodeUtil.isLatin(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); public void visit(Node n) { if (n.getType() == Token.NAME) { Node parent = n.getParent(); if (parent != null && parent

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.getType() == Token.VAR) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION))); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!NodeUtil.isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode() { // TODO(johnlenz): Why this instead of the more common "undefined"? return new Node(Token.VOID, Node.newNumber(0)); }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { nodeName.addChildrenToBack(value); } Node var = new Node(Token.VAR, nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * Whether a Node type is within the node tree. */ static boolean isNodeTypeReferenced(Node node, int type) { return isNodeTypeReferenced(node, type, Predicates.<Node>alwaysTrue()); } /** * Whether a Node type is within the node tree. */ static boolean isNodeTypeReferenced( Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount(Node node, int type) { return getCount(node, new MatchNodeType(type)); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNameNode(name), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name) { return isNameReferenced(node, name, Predicates.<Node>alwaysTrue()); }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> c != null; c = c.getNext()) { visitPostOrder(c, vistor, traverseChildrenPred); } } vistor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.getType() == Token.BLOCK); return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ static Node getFnParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBoolean

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Prop(Node.IS_CONSTANT_NAME); } /** * @param nameNode A name node * @return The JSDocInfo for the name node */ static JSDocInfo getInfoForNameNode(Node nameNode) { JSDocInfo info = null; Node parent = null; if (nameNode != null) { info = nameNode.getJSDocInfo(); parent = nameNode.getParent(); } if (info == null && parent != null && ((parent.getType() == Token.VAR && parent.hasOneChild()) || parent.getType() == Token.FUNCTION)) { info = parent.getJSDocInfo(); } return info; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); n = n.getParent(); } return sourceName; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Construct a tracer whose type is based on the short name of the object * @param object Object to use as type name * @param comment A comment * @return new Tracer. */ static Tracer shortName(Object object, String comment) { if (object == null) { return new Tracer(comment); } return new Tracer(object.getClass().getSimpleName(), comment); } /** * Converts 'v' to a string and pads it with up to 16 spaces for * improved alignment. * @param v The value to convert. * @param digits_column_width The desired with of the string. */ private static String longToPaddedString(long v, int digits_column_width) { int digit_width = numDigits(v); StringBuilder sb = new StringBuilder(); appendSpaces(sb, digits_column_width - digit_width); sb.append(v); return sb.toString(); } /** * Gets the number of digits in an integer when printed in base 10. Assumes * a positive integer. * @param n The value. * @return The number of digits in the string. */ private static int numDigits(long n) { int i = 0; do { i++; n = n / 10; } while (n > 0); return i; } /** * Gets a string of spaces of the length specified. * @param sb The string builder to append to. * @param numSpaces The number of spaces in the string. */ @VisibleForTesting static void appendSpaces(StringBuilder sb, int numSpaces) { if (numSpaces > 16) { logger.warning("Tracer.appendSpaces called with large numSpaces"); // Avoid long loop in case some bug in the caller numSpaces = 16; } while (numSpaces >= 5) { sb.append(" "); numSpaces -= 5; } // We know it's less than 5 now switch (numSpaces) { case 1: sb.append(" "); break; case 2: sb.append(" "); break; case 3: sb.append("

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Token.BLOCK && first.getNext().getChildCount() <= 1); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild(); if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 3); if (first.getNext().getType() != Token.EMPTY) { throw new Error("Catch conditions not suppored because I think" + " that it may be a netscape only feature."); } add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.NAME: if (first == null || first.getType() == Token.EMPTY) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.getType() == Token.COMMA) { addExpr(first, NodeUtil.precedence(Token.ASSIGN)); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(context)); } } break; case Token.ARRAYLIT: add("["); addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP)); add("]"); break; case Token.LP: add("("); addList(first); add(")"); break; case Token.COMMA: addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), p); cc.addOp(":", true); addExpr(last, p); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.GET_REF: add(first); break; case Token.REF_SPECIAL: Preconditions.checkState(childCount == 1); add(first); add("."); add((String) n.getProp(Node.NAME_PROP)); break; case Token.FUNCTION: Preconditions.checkState(childCount == 3);

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.SCRIPT: case Token.BLOCK: { boolean stripBlock = n.isSyntheticBlock() || ((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2)); if (!stripBlock) { cc.beginBlock(); } for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.getType() == Token.VAR) { cc.endStatement(); } if (c.getType() == Token.FUNCTION) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top level statements are more homogeneous. if (type == Token.SCRIPT) { cc.notePreferredLineBreak(); } } if (!stripBlock) { cc.endBlock(context == Context.STATEMENT); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.getType() == Token.VAR) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } break

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DOs. Node firstAndOnlyChild = getFirstNonEmptyChild(n); boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); if (alwaysWrapInBlock || firstAndOnlyChild.getType() == Token.FUNCTION || firstAndOnlyChild.getType() == Token.DO) { cc.beginBlock(); add(firstAndOnlyChild, Context.STATEMENT); cc.maybeLineBreak(); cc.endBlock(context == Context.STATEMENT); return; } else { // Continue with the only child. nodeToProcess = firstAndOnlyChild; } } } if (nodeToProcess.getType() == Token.EMPTY) { cc.endStatement(true); } else { add(nodeToProcess, context); // VAR doesn't include ';' since it gets used in expressions - so any // VAR in a statement context needs a call to endStatement() here. if (nodeToProcess.getType() == Token.VAR) { cc.endStatement(); } } } /** * Adds a node at the left-hand side of an expression. Unlike * {@link #addExpr(Node,int)}, this preserves information about the context. * * The left side of an expression is special because in the JavaScript * grammar, certain tokens may be parsed differently when they are at * the beginning of a statement. For example, "{}" is parsed as a block, * but "{'x': 'y'}" is parsed as an object literal. */ void addLeftExpr(Node n, int minPrecedence, Context context) { addExpr(n, minPrecedence, context); } void addExpr(Node n, int minPrecedence) { addExpr(n, minPrecedence, Context.OTHER); } private void addExpr(Node n, int minPrecedence, Context context) { if ((NodeUtil.precedence(n.getType()) < minPrecedence) || ((context == Context.IN_FOR_INIT_CLAUSE) && (n.getType() == Token.IN))

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> */ public class Scope implements StaticScope<JSType> { private final Map<String, Var> vars = new LinkedHashMap<String, Var>(); private final Scope parent; private final Node rootNode; /** The type of {@code this} in the current scope. */ private final ObjectType thisType; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; /** Stores info about a variable */ public static class Var implements StaticSlot<JSType> { /** name */ String name; /** Var node */ Node nameNode; /** * The variable's type. */ private JSType type; /** * The variable's doc info. */ private JSDocInfo info = null; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ CompilerInput input; /** Whether the variable is a define */ boolean isDefine; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ int index; /** The enclosing scope */ Scope scope; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred) { this.typeInferred = inferred; } /** * Gets the name of the variable. */ public String getName() { return name; } /** * Gets the parent of the name node. */ public Node getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns the index within the scope stack. * e.g. function Foo(a) { var b; function c(d) { } } * a = 0, b = 1, c = 2, d = 3 */ int getLocalVarIndex() { int num = index; Scope

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> s = scope.getParent(); if (s == null) { throw new IllegalArgumentException("Var is not local"); } while (s.getParent() != null) { num += s.getVarCount(); s = s.getParent(); } return num; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotaed by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); return parent.getType() == Token.FUNCTION ? parent : nameNode.getFirstChild(); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isInferred()}. */ public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ public JSDocInfo getJSDocInfo() { return info; } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ public boolean isTypeInferred() { return typeInferred; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> public String getInputName() { if (input == null) return "<non-file>"; else return input.getName(); } public boolean isNoShadow() { if (info != null && info.isNoShadow()) { return true; } else { return false; } } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name; } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType instanceof FunctionType) { thisType = ((FunctionType) nodeType).getTypeOfThis(); } else { thisType = parent.thisType; } this.isBottom = false; } /** * Creates a global Scope. * @param rootNode Typically the global BLOCK node. */ Scope(Node rootNode, AbstractCompiler compiler) { this.parent = null; this.rootNode = rootNode; thisType = compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS); this.isBottom = false; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param thisType the type of {@code this} in this scope */ Scope(Node rootNode, ObjectType thisType) { this.parent = null; this.rootNode = rootNode; this.thisType = thisType; this.isBottom = true; } /** Whether this is the bottom of the lattice. */ boolean isBottom

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ public ObjectType getTypeOfThis() { return thisType; } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred); var.name = name; var.nameNode = nameNode; var.type = type; var.scope = this; var.index = vars.size(); var.input = input; // native variables do not have a name node. // TODO(user): make Var abstract and have NativeVar, NormalVar. JSDocInfo info = NodeUtil.getInfoForNameNode(nameNode); var

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>.isDefine = info != null && info.isDefine(); var.info = info; vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } public StaticSlot<JSType> getSlot(String name) { return getVar(name); } public StaticSlot<JSType> getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); } else { return null; } } /** * Returns true if a variable is declared. */ public boolean isDeclared(String name, boolean recurse) { Scope scope = this; if (scope.vars.containsKey(name)) return true; if (scope.parent != null && recurse) { return scope.parent.isDeclared(name, recurse); } return false; } /** * Return an iterator over all of the variables declared in this scope. */ public Iterator<Var> getVars() { return vars.values().iterator(); } /** * Returns number of variables in this scope */ public int getVarCount() { return vars.size(); } /** * Returns whether this is the global scope. */ public boolean isGlobal() { return parent == null; } /** * Returns whether this is a local scope (i.e. not the global scope). */ public boolean isLocal() { return !isGlobal(); } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>BadModuleReference(name, ref); } } } } } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME, ref.module.getName(), name.declaration.module.getName(), name.fullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.getType() == Token.EXPR_RESULT) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return; } } compiler.report( JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING, name.fullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private static boolean propertyMustBeInitializedByFullName(Name name) { // If an object literal in the global namespace is never aliased, // then all of its properties must be defined using its full qualified // name. This implies that its properties must all be in the global // namespace as well. // // The same is not true for FUNCTION and OTHER types, because their // implicit prototypes have properties that are not captured by the global // namespace. return name.parent != null && name.parent.aliasingGets == 0 && name.parent.type == Name.Type.OBJECTLIT; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> default: Kit.codeBug(); } return null; } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof NumberNode && getDouble() == ((NumberNode) node).getDouble()); } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof StringNode && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key. The * QUOTED_PROP int property is only assigned to STRING tokens used as * object lit keys. * @return true if this was a quoted string key in an object literal. */ @Override public boolean isQuotedString

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) return null; Node n = first; while (n.next != child) { n = n.next; if (n == null) throw new RuntimeException("node is not a child"); } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) { last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = null; if (last == null) { first = last = child; return; } last.next = child; last = child; } public void addChildrenToFront(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSib = children.getLastSibling(); lastSib.next = first; first = children; if (last == null) { last = lastSib; } } public void addChildrenToBack(Node children) { for (Node child = children; child != null; child = child.next) { // Hmmm... IRFactory doesn't remove before calling this. Preconditions.checkArgument(child.parent == null); child.parent = this; } if (last != null) { last.next = children; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> last = children.getLastSibling(); if (first == null) { first = children; } } /** * Add 'child' before 'node'. */ public void addChildBefore(Node newChild, Node node) { Preconditions.checkArgument(node != null, "The existing child node of the parent should not be null."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); if (first == node) { newChild.parent = this; newChild.next = first; first = newChild; return; } Node prev = getChildBefore(node); addChildAfter(newChild, prev); } /** * Add 'child' after 'node'. */ public void addChildAfter(Node newChild, Node node) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); newChild.parent = this; newChild.next = node.next; node.next = newChild; if (last == node) { last = newChild; } } /** * Detach a child from its parent and siblings. */ public void removeChild(Node child) { Node prev = getChildBefore(child); if (prev == null) first = first.next; else prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; } /** * Detaches child from Node and replaces it with newChild. */ public void replaceChild(Node child, Node newChild) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(child); newChild.next = child.next; newChild.parent = this; if (child ==

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> first) { first = newChild; } else { Node prev = getChildBefore(child); prev.next = newChild; } if (child == last) last = newChild; child.next = null; child.parent = null; } public void replaceChildAfter(Node prevChild, Node newChild) { Preconditions.checkArgument(prevChild.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(prevChild); Node child = prevChild.next; newChild.next = child.next; newChild.parent = this; prevChild.next = newChild; if (child == last) last = newChild; child.next = null; child.parent = null; } private PropListItem lookupProperty(int propType) { PropListItem x = propListHead; while (x != null && propType != x.type) { x = x.next; } return x; } private PropListItem ensureProperty(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { item = new PropListItem(); item.type = propType; item.next = propListHead; propListHead = item; } return item; } public void removeProp(int propType) { PropListItem x = propListHead; if (x != null) { PropListItem prev = null; while (x.type != propType) { prev = x; x = x.next; if (x == null) { return; } } if (prev == null) { propListHead = x.next; } else { prev.next = x.next; } } } public Object getProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return null; } return item.objectValue; } public

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> boolean getBooleanProp(int propType) { return getIntProp(propType, 0) != 0; } public int getIntProp(int propType, int defaultValue) { PropListItem item = lookupProperty(propType); if (item == null) { return defaultValue; } return item.intValue; } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { Kit.codeBug(); } return item.intValue; } public void putProp(int propType, Object prop) { if (prop == null) { removeProp(propType); } else { PropListItem item = ensureProperty(propType); item.objectValue = prop; } } public void putBooleanProp(int propType, boolean prop) { putIntProp(propType, prop ? 1 : 0); } public void putIntProp(int propType, int prop) { PropListItem item = ensureProperty(propType); item.intValue = prop; } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.next) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.next) { count--; keys[count] = x.type; } Arrays.sort(keys); return keys; } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException( this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void set

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Double(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException( this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException( this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException( this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } return String.valueOf(type); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); sb.append(first.getString()); } else if (this instanceof ScriptOrFnNode) { ScriptOrFnNode sof = (ScriptOrFnNode)this; if (this instanceof FunctionNode) { FunctionNode fn = (FunctionNode)this; sb.append(' '); sb.append(fn.getFunctionName()); } if (printSource) { sb.append(" [source name: "); sb.append(sof.getSourceName()); sb.append("] [encoded source length: "); sb.append

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart()); sb.append("] [base line: "); sb.append(sof.getBaseLineno()); sb.append("] [end line: "); sb.append(sof.getEndLineno()); sb.append(']'); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { case TARGETBLOCK_PROP : // can't add this as it recurses value = "target block property"; break; case LOCAL_BLOCK_PROP : // can't add this as it is dull value = "last local block"; break; case ISNUMBER_PROP: switch (x.intValue) { case BOTH: value = "both"; break; case RIGHT: value = "right"; break; case LEFT: value = "left"; break; default: throw Kit.codeBug(); } break; case SPECIALCALL_PROP: switch (x.intValue) { case SPECIALCALL_EVAL: value = "eval"; break; case SPECIALCALL_WITH: value = "with"; break; default: // NON_SPECIALCALL should not be stored throw Kit.codeBug(); } break; default : Object obj = x.objectValue; if (obj != null) { value = obj.toString(); } else { value = String.valueOf(x.intValue); } break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString =

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> MAX_COLUMN_NUMBER. */ public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; /** * COLUMN_MASK stores a value where bits storing the column number * are set, and bits storing the line are not set. It's handy for * separating column number from line number. */ public static final int COLUMN_MASK = MAX_COLUMN_NUMBER; /** * Source position of this node. The position is encoded with the * column number in the low 12 bits of the integer, and the line * number in the rest. Create some handy constants so we can change this * size if we want. */ private int sourcePosition; private JSType jsType; private Node parent; //========================================================================== // Source position management public void setLineno(int lineno) { int charno = getCharno(); if (charno == -1) { charno = 0; } sourcePosition = mergeLineCharNo(lineno, charno); } public void setCharno(int charno) { sourcePosition = mergeLineCharNo(getLineno(), charno); } /** * Merges the line number and character number in one integer. The Character * number takes the first 12 bits and the line number takes the rest. If * the character number is greater than <code>2<sup>12</sup>-1</code> it is * adjusted to <code>2<sup>12</sup>-1</code>. */ protected static int mergeLineCharNo(int lineno, int charno) { if (lineno < 0 || charno < 0) { return -1; } else if ((charno & ~COLUMN_MASK) != 0) { return lineno << COLUMN_BITS | COLUMN_MASK; } else { return lineno << COLUMN_BITS | (charno & COLUMN_MASK); } } /** * Extracts the line number and character number from a merged line char * number (see {@link #mergeLineCharNo(int, int)}). */ protected static int extractLineno(int lineCharNo) { if (lineCharNo == -1) { return -1; }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } public boolean hasNext() { return current != null; } public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } public void remove() { throw new UnsupportedOperationException(); } } //========================================================================== // Accessors public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while(node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } public Iterator<Node> iterator() { return new Iterator<Node>() { public boolean hasNext() { return cur != null; } public Node next() { if (!hasNext()) throw new NoSuchElementException(); Node n = cur; cur = cur.getParent(); return n; } public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * @return Whether the node has exactly one child. */ public boolean hasOneChild() { return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * @return Whether the node more than

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>); if (res != null) { return res; } } return res; } /** * Checks if the subtree under this node is the same as another subtree * including types. Returns null if it's equal, or a message describing the * differences. */ public boolean checkTreeTypeAwareEqualsSilent(Node node2) { return checkTreeTypeAwareEqualsImpl(node2) == null; } /** * Compare this node to node2 recursively and return the first pair * of nodes that differs doing a preorder depth-first traversal. * Package private for testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getClass() == node2.getClass() && Objects.equal(jsType, node2.getJSType())) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.EOF: return "eof"; case Token.EOL: return "eol"; case Token.ENTERWITH: return "enterwith"; case Token.LEAVEWITH: return "leavewith"; case Token.RETURN: return "return"; case Token.GOTO: return "goto"; case Token.IFEQ: return "ifeq"; case Token.IFNE: return "ifne"; case Token.SETNAME: return "setname"; case Token.BITOR: return "bitor"; case Token.BITXOR: return "bitxor"; case Token.BIT

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>NAME: return "typeofname"; case Token.THISFN: return "thisfn"; case Token.SEMI: return "semi"; case Token.LB: return "lb"; case Token.RB: return "rb"; case Token.LC: return "lc"; case Token.RC: return "rc"; case Token.LP: return "lp"; case Token.RP: return "rp"; case Token.COMMA: return "comma"; case Token.ASSIGN: return "assign"; case Token.ASSIGN_BITOR: return "assign_bitor"; case Token.ASSIGN_BITXOR: return "assign_bitxor"; case Token.ASSIGN_BITAND: return "assign_bitand"; case Token.ASSIGN_LSH: return "assign_lsh"; case Token.ASSIGN_RSH: return "assign_rsh"; case Token.ASSIGN_URSH: return "assign_ursh"; case Token.ASSIGN_ADD: return "assign_add"; case Token.ASSIGN_SUB: return "assign_sub"; case Token.ASSIGN_MUL: return "assign_mul"; case Token.ASSIGN_DIV: return "assign_div"; case Token.ASSIGN_MOD: return "assign_mod"; case Token.HOOK: return "hook"; case Token.COLON: return "colon"; case Token.OR: return "or"; case Token.AND: return "and"; case Token.INC: return "inc"; case Token.DEC: return "dec"; case Token.DOT: return "dot"; case Token.FUNCTION: return "function"; case Token.EXPORT: return "export"; case Token.IMPORT: return "import"; case Token.IF: return "if"; case Token.ELSE: return "else"; case Token.SWITCH: return "switch"; case Token.CASE: return "case"; case Token.DEFAULT: return "default"; case Token.WHILE: return "while"; case Token.DO: return "do"; case Token.FOR: return "for"; case Token.BREAK: return "break";

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> case Token.CONTINUE: return "continue"; case Token.VAR: return "var"; case Token.WITH: return "with"; case Token.CATCH: return "catch"; case Token.FINALLY: return "finally"; case Token.RESERVED: return "reserved"; case Token.NOT: return "not"; case Token.VOID: return "void"; case Token.BLOCK: return "block"; case Token.ARRAYLIT: return "arraylit"; case Token.OBJECTLIT: return "objectlit"; case Token.LABEL: return "label"; case Token.TARGET: return "target"; case Token.LOOP: return "loop"; case Token.EXPR_VOID: return "expr_void"; case Token.EXPR_RESULT: return "expr_result"; case Token.JSR: return "jsr"; case Token.SCRIPT: return "script"; case Token.EMPTY: return "empty"; case Token.GET_REF: return "get_ref"; case Token.REF_SPECIAL: return "ref_special"; } return "<unknown="+token+">"; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { if (type == Token.ARRAYLIT) { try { int[] indices1 = (int[])getProp(Node.SKIP_INDEXES_PROP); int[] indices2 = (int[])node.getProp(Node.SKIP_INDEXES_PROP); if (indices1 == null) { if (indices2 != null) return false; } else if (indices2 == null) { return false; } else if (indices1.length != indices2.length) { return false; } else { for (int i = 0; i < indices1.length; i++) { if (indices1[i] != indices2[i]) return false; } } } catch (Exception e) { return false; } } else if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDECR_PROP, 0); int post2 =

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>: case Token.ELSE: case Token.SWITCH: case Token.WHILE: case Token.DO: case Token.FOR: case Token.BREAK: case Token.CONTINUE: case Token.VAR: case Token.CONST: case Token.WITH: case Token.CATCH: case Token.FINALLY: case Token.BLOCK: case Token.LABEL: case Token.TARGET: case Token.LOOP: case Token.JSR: case Token.SETPROP_OP: case Token.SETELEM_OP: case Token.LOCAL_BLOCK: case Token.SET_REF_OP: return true; default: return false; } } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such * as <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name * without a "this" reference, such as <code>a.b.c</code>, but not * <code>this.a</code>. */ public boolean isUn

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>scopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } //========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null; ) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its * children. */ public Node cloneNode() { Node result; try { result = (Node) super.clone(); result.next = null;

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public Node cloneTree() { Node result = cloneNode(); for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { Node n2clone = n2.cloneTree(); n2clone.parent = result; if (result.last != null) { result.last.next = n2clone; } if (result.first == null) { result.first = n2clone; } result.last = n2clone; } return result; } /** * Copies source file and name information from the other * node given to the current node. Used for maintaining * debug information across node append and remove operations. */ public void copyInformationFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(SOURCEFILE_PROP) == null) { putProp(SOURCEFILE_PROP, other.getProp(SOURCEFILE_PROP)); sourcePosition = other.sourcePosition; } } /** * Copies source file and name information from the other node to the * entire tree rooted at this node. */ public void copyInformationFromForTree(Node other) { copyInformationFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } } //========================================================================== // Custom annotations public JSType getJSType() { return jsType; } public void setJSType(JSType jsType) { this.jsType = jsType; } public FileLevelJsDocBuilder getJsDocBuilderForNode() { return new FileLevelJsDocBuilder(); } /** * An inner class that provides back-door access to the license * property of the JSDocInfo

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. builder.addProperty(fieldName, fieldType); } return builder.build(); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes(n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodes(n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable return createNullableType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate(createFromTypeNodes(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if ((namedType

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> instanceof ObjectType) && !(enumTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodes( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodes( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createNullableType(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; Node current = n.getFirstChild(); if (current.getType() == Token.THIS) { Node thisNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodes(thisNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.thisnotobject"), sourceName, thisNode.getLineno(), "", thisNode.getCharno()); } current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.LP) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodes( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodes(arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), "", arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodes(current, sourceName, scope); return new FunctionType(this, null, null, paramBuilder.build(), returnType, thisType, null); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() { templateTypeName = null; templateType = null; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> * */ final class ArrowType extends JSType { private static final long serialVersionUID = 1L; final Node parameters; JSType returnType; ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType) { super(registry); this.parameters = parameters; this.returnType = returnType; } @Override public boolean isSubtype(JSType other) { if (!(other instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) other; // this.returnType <: that.returnType (covariant) // If the return type is null, this is equivalent to unknown so we do not // base our decision on that. if (this.returnType != null && that.returnType != null && !this.returnType.isSubtype(that.returnType)) { return false; } // that.paramType[i] <: this.paramType[i] (contravariant) // TODO(nicksantos): This is incorrect. It should be invariant. // Follow up with closure team on how to fix this without everyone // hating on us. // // If the parameter list is null, this is equivalent of ?... so we do not // base our decision on that. if (this.parameters != null && that.parameters != null) { Node thisParam = parameters.getFirstChild(); Node thatParam = that.parameters.getFirstChild(); while (thisParam != null && thatParam != null) { JSType thisParamType = thisParam.getJSType(); if (thisParamType != null) { JSType thatParamType = thatParam.getJSType(); if (thatParamType == null || !thatParamType.isSubtype(thisParamType)) { return false; } } boolean thisIsVarArgs = thisParam.isVarArgs(); boolean thatIsVarArgs = thatParam.isVarArgs(); // don't advance if we have variable arguments if (!thisIsVarArgs) { thisParam = thisParam.getNext(); } if (!thatIsVarArgs) { thatParam = thatParam.getNext(); } // both var_args indicates the end if (thisIsVarArgs && thatIsVarArgs) { thisParam = null; that

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>Param = null; } } // Right now, the parser's type system doesn't have a good way // to model optional arguments. // // Suppose we have // function f(number, number) {} // function g(number) {} // If the second arg of f is optional, then f is a subtype of g, // but g is not a subtype of f. // If the second arg of f is required, then g is a subtype of f, // but f is not a subtype of g. // // Until we model optional params, let's just punt on this. // If one type has more arguments than the other, we won't check them. // // NOTE(nicksantos): This is described in Draft 2 of the ES4 spec, // Section 3.4.6: Subtyping Function Types. It seems really // strange but I haven't thought a lot about the implementation. } return true; } @Override public boolean equals(Object object) { // Please keep this method in sync with the hashCode() method below. if (!(object instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) object; // if both return types are specified, then they should be equal if (returnType == null) { if (that.returnType != null) { return false; } } else { if (that.returnType == null) { return false; } if (!returnType.equals(that.returnType)) { return false; } } // if both types include parameters, the lists should be the same if (parameters == null) { return that.parameters == null; } else if (that.parameters == null) { return false; } Node thisParam = parameters.getFirstChild(); Node otherParam = that.parameters.getFirstChild(); while (thisParam != null && otherParam != null) { JSType thisParamType = thisParam.getJSType(); JSType otherParamType = otherParam.getJSType(); if (thisParamType != null) { // Both parameter lists give a type for this param, it should be equal if (otherParamType != null && !thisParamType.equals(otherParamType)) {

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> return false; } } else { if (otherParamType != null) { return false; } } thisParam = thisParam.getNext(); otherParam = otherParam.getNext(); } // One of the parameters is null, so the types are only equal if both // parameter lists are null (they are equal). return thisParam == otherParam; } @Override public int hashCode() { int hashCode = 0; if (returnType != null) { hashCode += returnType.hashCode(); } if (parameters != null) { Node param = parameters.getFirstChild(); while (param != null) { JSType paramType = param.getJSType(); if (paramType != null) { hashCode += paramType.hashCode(); } param = param.getNext(); } } return hashCode; } @Override public JSType getLeastSupertype(JSType that) { throw new UnsupportedOperationException(); } @Override public JSType getGreatestSubtype(JSType that) { throw new UnsupportedOperationException(); } @Override public TernaryValue testForEquality(JSType that) { throw new UnsupportedOperationException(); } @Override public <T> T visit(Visitor<T> visitor) { throw new UnsupportedOperationException(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { returnType = safeResolve(returnType, t, scope); if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); } } return this; } }

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Roger Lawrence * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; public class FunctionNode extends ScriptOrFnNode { private static final long serialVersionUID = 1L; public FunctionNode(String name) { super(Token.FUNCTION); functionName = name; } public FunctionNode(String name, int lineno, int charno) { super(Token.FUNCTION, lineno, charno); functionName = name; } public String getFunctionName() { return

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> null} to indicate * that the return type is unknown. * @param typeOfThis The type of {@code this} in non-constructors. May be * {@code null} to indicate that the type of {@code this} is unknown. * @param templateTypeName The template type name or {@code null}. */ public FunctionType(JSTypeRegistry registry, String name, Node source, Node parameters, JSType returnType, ObjectType typeOfThis, String templateTypeName) { this(registry, name, source, parameters, returnType, typeOfThis, templateTypeName, false, false); } /** Creates an instance for a function that might be a constructor. */ FunctionType(JSTypeRegistry registry, String name, Node source, Node parameters, JSType returnType, ObjectType typeOfThis, String templateTypeName, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); this.source = source; this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY; if (isConstructor) { this.typeOfThis = typeOfThis != null && typeOfThis.isNoObjectType() ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } // The call type should be set up last because we are calling getReturnType, // which may be overloaded and depend on other properties being set. this.call = new ArrowType(registry, parameters, (returnType == null ? getReturnType() : returnType)); this.templateTypeName = templateTypeName; } /** Creates an instance for a function that is an interface. */ FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = null; this

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> implemented directly by a class or its superclass. */ public Iterable<ObjectType> getImplementedInterfaces() { FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor == null) { return implementedInterfaces; } else { return Iterables.concat( implementedInterfaces, superCtor.getImplementedInterfaces()); } } public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) { // Records this type for each implemented interface. for (ObjectType type : implementedInterfaces) { registry.registerTypeImplementingInterface(this, type); } this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); } @Override public boolean hasProperty(String name) { return super.hasProperty(name) || "prototype".equals(name); } @Override public boolean hasOwnProperty(String name) { return super.hasOwnProperty(name) || "prototype".equals(name); } @Override public JSType getPropertyType(String name) { if ("prototype".equals(name)) { return getPrototype(); } else { if (!hasOwnProperty(name)) { if ("call".equals(name)) { // Define the "call" function lazily. Node params = getParametersNode(); if (params == null) { // If there's no params array, don't do any type-checking // in this CALL function. defineDeclaredProperty(name, new FunctionType(registry, null, null, null, getReturnType()), false); } else { params = params.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); thisTypeNode.setOptionalArg(true); defineDeclaredProperty(name, new FunctionType(registry, null, null, params, getReturnType()), false); } } else if ("apply".equals(name)) { // Define the "apply" function lazily. FunctionParamBuilder builder = new FunctionParamBuilder(registry); // Ecma-262 says that apply's second argument must be an Array // or an arguments object. We don't model the arguments object

Closure, 139

<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); n.addChildBefore(new Node(Token.EMPTY), expr); n.addChildAfter(new Node(Token.EMPTY), expr); reportCodeChange("WHILE node"); } break; <CHANGES> <CHANGEE> } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ <CHANGES> <CHANGEE> /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ <CHANGES> <CHANGEE> // Prepare a spot for the function. <CHANGES> <CHANGEE> // Prepare the function <CHANGES> <CHANGEE> // Move the function <CHANGES> <CHANGEE> /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler<SCANS> new StringBuilder(32); b.append("function ("); int paramNum = (call == null || call.parameters == null) ? 0 : call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(typeOfThis.toString()); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); while (p != null) { b.append(", "); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); } } b.append(")"); if (call != null && call.returnType != null) { b.append(": "); b.append(call.returnType); } return b.toString(); } /** Gets the string representation of a var args param. */ private void appendVarArgsString(StringBuilder builder, JSType paramType) { if (paramType.isUnionType()) { // Remove the optionalness from the var arg. paramType = ((UnionType) paramType).getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append("...[").append(paramType.toString()).append("]"); } /** * A function is a subtype of another if their call methods are related via * subtyping and {@code this} is a subtype of {@code that} with regard to * the prototype chain. */ @Override public boolean isSubtype(JSType that) { if (this.equals(that)) { return true; } if (that.isFunctionType()) { if (((FunctionType) that).isInterface()) { // Any function can be assigned to an interface function. return true; } if (this